LCOV - code coverage report
Current view: top level - src/system - SystemAtom.test.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 224 252 88.9 %
Date: 2026-04-17 09:20:02 Functions: 8 9 88.9 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2024 PairInteraction Developers
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #include "pairinteraction/system/SystemAtom.hpp"
       5             : 
       6             : #include "pairinteraction/basis/BasisAtom.hpp"
       7             : #include "pairinteraction/basis/BasisAtomCreator.hpp"
       8             : #include "pairinteraction/database/Database.hpp"
       9             : #include "pairinteraction/diagonalize/DiagonalizerEigen.hpp"
      10             : #include "pairinteraction/diagonalize/DiagonalizerFeast.hpp"
      11             : #include "pairinteraction/diagonalize/DiagonalizerLapackeEvd.hpp"
      12             : #include "pairinteraction/diagonalize/DiagonalizerLapackeEvr.hpp"
      13             : #include "pairinteraction/diagonalize/diagonalize.hpp"
      14             : #include "pairinteraction/enums/FloatType.hpp"
      15             : #include "pairinteraction/ket/KetAtom.hpp"
      16             : #include "pairinteraction/ket/KetAtomCreator.hpp"
      17             : 
      18             : #include <Eigen/Eigenvalues>
      19             : #include <cmath>
      20             : #include <doctest/doctest.h>
      21             : #include <fmt/ranges.h>
      22             : 
      23             : namespace pairinteraction {
      24             : 
      25             : constexpr double VOLT_PER_CM_IN_ATOMIC_UNITS = 1 / 5.14220675112e9;
      26             : constexpr double UM_IN_ATOMIC_UNITS = 1 / 5.29177210544e-5;
      27             : constexpr double HARTREE_IN_GHZ = 6579683.920501762;
      28             : 
      29           1 : DOCTEST_TEST_CASE("construct and diagonalize a small Hamiltonian") {
      30           1 :     auto &database = Database::get_global_instance();
      31           1 :     auto diagonalizer = DiagonalizerEigen<double>();
      32             : 
      33           0 :     auto ket1 = KetAtomCreator()
      34           2 :                     .set_species("Rb")
      35           1 :                     .set_quantum_number_n(60)
      36           1 :                     .set_quantum_number_l(0)
      37           1 :                     .set_quantum_number_j(0.5)
      38           1 :                     .set_quantum_number_m(0.5)
      39           1 :                     .create(database);
      40           0 :     auto ket2 = KetAtomCreator()
      41           2 :                     .set_species("Rb")
      42           1 :                     .set_quantum_number_n(60)
      43           1 :                     .set_quantum_number_l(1)
      44           1 :                     .set_quantum_number_j(0.5)
      45           1 :                     .set_quantum_number_m(0.5)
      46           1 :                     .create(database);
      47           1 :     auto basis = BasisAtomCreator<double>().append_ket(ket1).append_ket(ket2).create(database);
      48             : 
      49           1 :     auto system = SystemAtom<double>(basis);
      50           1 :     system.set_electric_field({0, 0, 0.0001});
      51             : 
      52           1 :     Eigen::MatrixXd tmp = Eigen::MatrixXd(1e5 * system.get_matrix()).array().round() / 1e5;
      53           1 :     std::vector<double> matrix_vector(tmp.data(), tmp.data() + tmp.size());
      54           3 :     DOCTEST_MESSAGE(fmt::format("Constructed: {}", fmt::join(matrix_vector, ", ")));
      55             : 
      56           1 :     system.diagonalize(diagonalizer);
      57           1 :     tmp = Eigen::MatrixXd(1e5 * system.get_matrix()).array().round() / 1e5;
      58           1 :     matrix_vector = std::vector<double>(tmp.data(), tmp.data() + tmp.size());
      59           3 :     DOCTEST_MESSAGE(fmt::format("Diagonalized: {}", fmt::join(matrix_vector, ", ")));
      60             : 
      61           1 :     Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigensolver;
      62           1 :     eigensolver.compute(system.get_matrix());
      63           1 :     auto eigenenergies_eigen = eigensolver.eigenvalues();
      64           1 :     auto eigenenergies_pairinteraction = system.get_eigenenergies();
      65           3 :     for (int i = 0; i < eigenenergies_eigen.size(); ++i) {
      66           2 :         DOCTEST_CHECK(std::abs(eigenenergies_eigen(i) - eigenenergies_pairinteraction(i)) < 1e-10);
      67             :     }
      68           1 : }
      69             : 
      70           1 : DOCTEST_TEST_CASE("construct and diagonalize two Hamiltonians in parallel") {
      71           1 :     auto &database = Database::get_global_instance();
      72           1 :     auto diagonalizer = DiagonalizerEigen<std::complex<double>>();
      73             : 
      74           0 :     auto basis = BasisAtomCreator<std::complex<double>>()
      75           2 :                      .set_species("Rb")
      76           1 :                      .restrict_quantum_number_n(59, 61)
      77           1 :                      .restrict_quantum_number_l(0, 1)
      78           1 :                      .create(database);
      79             : 
      80           1 :     auto system1 = SystemAtom<std::complex<double>>(basis);
      81           1 :     system1.set_electric_field({0, 0, 0.0001});
      82             : 
      83           1 :     auto system2 = SystemAtom<std::complex<double>>(basis);
      84           1 :     system2.set_electric_field({0, 0, 0.0002});
      85             : 
      86           1 :     diagonalize<SystemAtom<std::complex<double>>>({system1, system2}, diagonalizer);
      87             : 
      88           1 :     auto matrix1 = system1.get_matrix();
      89           1 :     auto matrix2 = system2.get_matrix();
      90          25 :     for (int i = 0; i < matrix1.rows(); ++i) {
      91         600 :         for (int j = 0; j < matrix1.cols(); ++j) {
      92         576 :             if (i != j) {
      93         552 :                 DOCTEST_CHECK(std::abs(matrix1.coeff(i, j)) < 1e-10);
      94         552 :                 DOCTEST_CHECK(std::abs(matrix2.coeff(i, j)) < 1e-10);
      95             :             }
      96             :         }
      97             :     }
      98           1 : }
      99             : 
     100           1 : DOCTEST_TEST_CASE("construct an atomic Hamiltonian in a non-canonical atomic basis") {
     101           1 :     auto &database = Database::get_global_instance();
     102             : 
     103           0 :     auto basis = BasisAtomCreator<double>()
     104           2 :                      .set_species("Rb")
     105           1 :                      .restrict_quantum_number_n(60, 61)
     106           1 :                      .restrict_quantum_number_l(0, 1)
     107           1 :                      .restrict_quantum_number_m(0.5, 0.5)
     108           1 :                      .create(database);
     109           1 :     DOCTEST_REQUIRE(basis->get_number_of_states() >= 2);
     110             : 
     111           1 :     SystemAtom<double> reference_system(basis);
     112           1 :     reference_system.set_electric_field({0, 0, 1 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     113           1 :     const auto &reference_matrix = reference_system.get_matrix();
     114             : 
     115             :     Eigen::SparseMatrix<double, Eigen::RowMajor> transformation(
     116           1 :         static_cast<Eigen::Index>(basis->get_number_of_states()),
     117           2 :         static_cast<Eigen::Index>(basis->get_number_of_states()));
     118           1 :     transformation.setIdentity();
     119             : 
     120           1 :     double inverse_sqrt_two = 1 / std::sqrt(2.0);
     121           1 :     transformation.coeffRef(0, 0) = inverse_sqrt_two;
     122           1 :     transformation.coeffRef(1, 0) = inverse_sqrt_two;
     123           1 :     transformation.coeffRef(0, 1) = inverse_sqrt_two;
     124           1 :     transformation.coeffRef(1, 1) = -inverse_sqrt_two;
     125           1 :     transformation.makeCompressed();
     126             : 
     127           1 :     auto transformed_basis = basis->transformed(transformation);
     128           1 :     SystemAtom<double> transformed_system(transformed_basis);
     129           1 :     transformed_system.set_electric_field({0, 0, 1 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     130             : 
     131             :     Eigen::SparseMatrix<double, Eigen::RowMajor> expected_matrix =
     132           1 :         transformation.adjoint() * reference_matrix * transformation;
     133             : 
     134           1 :     DOCTEST_CHECK(transformed_system.get_matrix().isApprox(expected_matrix, 1e-11));
     135           1 : }
     136             : 
     137           0 : DOCTEST_TEST_CASE("construct and diagonalize multiple Hamiltonians in parallel" *
     138             :                   doctest::skip(true)) {
     139             :     // TODO For a slow database, the fast parallelized construction of the tiny Hamiltonians seems
     140             :     // to lead to the wrong Hamiltonians (visible in the warning "The floating point error (5e-324
     141             :     // Hartree) is similar or larger than error estimated from the specified tolerance (0
     142             :     // Hartree)."). This could be caused by an issue with thread safety or memory access.
     143           0 :     int n = 10;
     144             : 
     145           0 :     auto &database = Database::get_global_instance();
     146           0 :     auto diagonalizer = DiagonalizerEigen<std::complex<double>>();
     147             : 
     148           0 :     auto basis = BasisAtomCreator<std::complex<double>>()
     149           0 :                      .set_species("Sr87_mqdt")
     150           0 :                      .restrict_quantum_number_nu(60, 61)
     151           0 :                      .restrict_quantum_number_l(0, 1)
     152           0 :                      .create(database);
     153             : 
     154           0 :     std::vector<SystemAtom<std::complex<double>>> systems;
     155           0 :     systems.reserve(n);
     156           0 :     for (int i = 0; i < n; ++i) {
     157           0 :         auto system = SystemAtom<std::complex<double>>(basis);
     158           0 :         system.set_electric_field({0, 0, 0.0001 * i});
     159           0 :         systems.push_back(std::move(system));
     160           0 :     }
     161             : 
     162           0 :     DOCTEST_MESSAGE("Basis size: ", basis->get_number_of_states());
     163             : 
     164           0 :     diagonalize<SystemAtom<std::complex<double>>>(systems, diagonalizer);
     165           0 : }
     166             : 
     167           2 : DOCTEST_TEST_CASE("construct and diagonalize a Hamiltonian using different methods") {
     168           2 :     auto &database = Database::get_global_instance();
     169             : 
     170           0 :     auto basis = BasisAtomCreator<std::complex<double>>()
     171           4 :                      .set_species("Rb")
     172           2 :                      .restrict_quantum_number_n(60, 61)
     173           2 :                      .restrict_quantum_number_l(0, 1)
     174           2 :                      .create(database);
     175             : 
     176             :     // Diagonalize using the Eigen library
     177           2 :     auto system = SystemAtom<std::complex<double>>(basis);
     178           2 :     system.set_electric_field({1 * VOLT_PER_CM_IN_ATOMIC_UNITS, 2 * VOLT_PER_CM_IN_ATOMIC_UNITS,
     179             :                                3 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     180             : 
     181           2 :     Eigen::MatrixXcd matrix = system.get_matrix();
     182           2 :     Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> eigensolver;
     183           2 :     eigensolver.compute(matrix);
     184           2 :     auto eigenenergies_eigen = eigensolver.eigenvalues();
     185             : 
     186             :     // Create diagonalizers
     187           2 :     std::vector<std::unique_ptr<DiagonalizerInterface<std::complex<double>>>> diagonalizers;
     188           2 :     std::vector<double> rtols;
     189           2 :     double eps{};
     190           2 :     DOCTEST_SUBCASE("Double precision") {
     191           1 :         diagonalizers.push_back(std::make_unique<DiagonalizerEigen<std::complex<double>>>());
     192             : #ifdef WITH_LAPACKE
     193           1 :         diagonalizers.push_back(std::make_unique<DiagonalizerLapackeEvd<std::complex<double>>>());
     194           1 :         diagonalizers.push_back(std::make_unique<DiagonalizerLapackeEvr<std::complex<double>>>());
     195             : #endif
     196             : #ifdef WITH_MKL
     197           1 :         diagonalizers.push_back(std::make_unique<DiagonalizerFeast<std::complex<double>>>(300));
     198             : #endif
     199           1 :         rtols = {1e-1, 1e-6, 1e-14};
     200           1 :         eps = std::numeric_limits<double>::epsilon();
     201           2 :     }
     202             : 
     203           2 :     DOCTEST_SUBCASE("Single precision") {
     204           1 :         diagonalizers.push_back(
     205           2 :             std::make_unique<DiagonalizerEigen<std::complex<double>>>(FloatType::FLOAT32));
     206             : #ifdef WITH_LAPACKE
     207           1 :         diagonalizers.push_back(
     208           2 :             std::make_unique<DiagonalizerLapackeEvd<std::complex<double>>>(FloatType::FLOAT32));
     209           1 :         diagonalizers.push_back(
     210           2 :             std::make_unique<DiagonalizerLapackeEvr<std::complex<double>>>(FloatType::FLOAT32));
     211             : #endif
     212             : #ifdef WITH_MKL
     213           1 :         diagonalizers.push_back(
     214           2 :             std::make_unique<DiagonalizerFeast<std::complex<double>>>(300, FloatType::FLOAT32));
     215             : #endif
     216           1 :         rtols = {1e-1, 1e-6};
     217           1 :         eps = std::numeric_limits<float>::epsilon();
     218           2 :     }
     219             : 
     220             :     // Diagonalize using pairinteraction
     221           7 :     for (double rtol_eigenenergies : rtols) {
     222             :         double atol_eigenvectors =
     223           5 :             std::max(0.5 * rtol_eigenenergies / std::sqrt(basis->get_number_of_states()), 5 * eps);
     224           5 :         DOCTEST_MESSAGE("Precision: " << rtol_eigenenergies << " (rtol eigenenergies), "
     225             :                                       << atol_eigenvectors << " (atol eigenvectors)");
     226             : 
     227          25 :         for (const auto &diagonalizer : diagonalizers) {
     228          20 :             auto system = SystemAtom<std::complex<double>>(basis);
     229          20 :             system.set_electric_field({1 * VOLT_PER_CM_IN_ATOMIC_UNITS,
     230             :                                        2 * VOLT_PER_CM_IN_ATOMIC_UNITS,
     231             :                                        3 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     232             : 
     233             :             // We specify a search interval because this is required if the FEAST routine is
     234             :             // used. To avoid overflows, the interval ranges from half the smallest possible
     235             :             // value to half the largest possible value.
     236          20 :             system.diagonalize(*diagonalizer, std::numeric_limits<float>::lowest() / 2,
     237          20 :                                std::numeric_limits<float>::max() / 2, rtol_eigenenergies);
     238          20 :             auto eigenenergies_pairinteraction = system.get_eigenenergies();
     239          20 :             auto eigenvectors_pairinteraction = system.get_eigenbasis()->get_coefficients();
     240             : 
     241          20 :             DOCTEST_CHECK(
     242             :                 (eigenenergies_eigen - eigenenergies_pairinteraction).array().abs().maxCoeff() <
     243             :                 rtol_eigenenergies * matrix.norm());
     244             : 
     245         340 :             for (int i = 0; i < eigenvectors_pairinteraction.cols(); ++i) {
     246         320 :                 DOCTEST_CHECK(abs(1 - eigenvectors_pairinteraction.col(i).norm()) <
     247             :                               atol_eigenvectors * eigenvectors_pairinteraction.rows());
     248             :             }
     249          20 :         }
     250             :     }
     251           2 : }
     252             : 
     253           1 : DOCTEST_TEST_CASE("construct and diagonalize a Hamiltonian with energy restrictions") {
     254           1 :     double min_energy = 0.153355;
     255           1 :     double max_energy = 0.153360;
     256             : 
     257           1 :     auto &database = Database::get_global_instance();
     258             : 
     259           0 :     auto basis = BasisAtomCreator<double>()
     260           2 :                      .set_species("Rb")
     261           1 :                      .restrict_quantum_number_n(58, 62)
     262           1 :                      .restrict_quantum_number_l(0, 1)
     263           1 :                      .create(database);
     264             : 
     265             :     // Diagonalize using the Eigen library
     266           1 :     auto system = SystemAtom<double>(basis);
     267           1 :     system.set_electric_field(
     268             :         {1 * VOLT_PER_CM_IN_ATOMIC_UNITS, 0, 1 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     269             : 
     270           1 :     Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigensolver;
     271           1 :     eigensolver.compute(system.get_matrix());
     272           1 :     auto eigenenergies_all = eigensolver.eigenvalues();
     273           1 :     std::vector<double> eigenenergies_eigen;
     274          41 :     for (int i = 0; i < eigenenergies_all.size(); ++i) {
     275          40 :         if (eigenenergies_all[i] > min_energy && eigenenergies_all[i] < max_energy) {
     276           8 :             eigenenergies_eigen.push_back(eigenenergies_all[i]);
     277             :         }
     278             :     }
     279             : 
     280             :     // Create diagonalizer
     281           1 :     std::vector<std::unique_ptr<DiagonalizerInterface<double>>> diagonalizers;
     282           1 :     diagonalizers.push_back(std::make_unique<DiagonalizerEigen<double>>(FloatType::FLOAT64));
     283             : #ifdef WITH_LAPACKE
     284           1 :     diagonalizers.push_back(std::make_unique<DiagonalizerLapackeEvd<double>>(FloatType::FLOAT64));
     285           1 :     diagonalizers.push_back(std::make_unique<DiagonalizerLapackeEvr<double>>(FloatType::FLOAT64));
     286             : #endif
     287             : #ifdef WITH_MKL
     288           1 :     diagonalizers.push_back(std::make_unique<DiagonalizerFeast<double>>(10, FloatType::FLOAT64));
     289             : #endif
     290             : 
     291             :     // Diagonalize using pairinteraction
     292           5 :     for (const auto &diagonalizer : diagonalizers) {
     293           4 :         auto system = SystemAtom<double>(basis);
     294           4 :         system.set_electric_field(
     295             :             {1 * VOLT_PER_CM_IN_ATOMIC_UNITS, 0, 1 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     296             : 
     297           4 :         system.diagonalize(*diagonalizer, min_energy, max_energy, 1e-6);
     298           4 :         auto eigenenergies_pairinteraction = system.get_eigenenergies();
     299             : 
     300           4 :         Eigen::MatrixXd tmp = (1e5 * eigenenergies_pairinteraction).array().round() / 1e5;
     301           4 :         std::vector<double> eigenenergies_vector(tmp.data(), tmp.data() + tmp.size());
     302          12 :         DOCTEST_MESSAGE(fmt::format("Eigenenergies: {}", fmt::join(eigenenergies_vector, ", ")));
     303             : 
     304           4 :         DOCTEST_CHECK(eigenenergies_eigen.size() == 8);
     305           4 :         DOCTEST_CHECK(eigenenergies_pairinteraction.size() == 8);
     306          36 :         for (size_t i = 0; i < eigenenergies_eigen.size(); ++i) {
     307          32 :             DOCTEST_CHECK(std::abs(eigenenergies_eigen[i] - eigenenergies_pairinteraction[i]) <
     308             :                           1e-10);
     309             :         }
     310           4 :     }
     311           1 : }
     312             : 
     313             : #ifdef WITH_MKL
     314             : #include <Eigen/Dense>
     315             : #include <mkl.h>
     316           1 : DOCTEST_TEST_CASE("diagonalization with mkl") {
     317             :     // We loop several times to check for memory errors
     318          11 :     for (size_t i = 0; i < 10; ++i) {
     319             :         // Create a symmetric matrix
     320          10 :         int n = 100;
     321          10 :         Eigen::MatrixXd matrix = Eigen::MatrixXd::Random(n, n);
     322          10 :         matrix = (matrix + matrix.transpose()).eval();
     323          10 :         Eigen::VectorXd eigenenergies(n);
     324             : 
     325             :         // Diagonalize the matrix
     326             :         int info =
     327          10 :             LAPACKE_dsyev(LAPACK_COL_MAJOR, 'V', 'U', n, matrix.data(), n, eigenenergies.data());
     328          10 :         DOCTEST_CHECK(info == 0);
     329          10 :     }
     330           1 : }
     331             : #endif
     332             : 
     333           1 : DOCTEST_TEST_CASE("handle it gracefully if no eigenenergies are within energy restrictions") {
     334           1 :     double min_energy = -1;
     335           1 :     double max_energy = -1;
     336             : 
     337           1 :     auto &database = Database::get_global_instance();
     338             : 
     339           0 :     auto basis = BasisAtomCreator<double>()
     340           2 :                      .set_species("Rb")
     341           1 :                      .restrict_quantum_number_n(58, 62)
     342           1 :                      .restrict_quantum_number_l(0, 1)
     343           1 :                      .create(database);
     344             : 
     345           1 :     std::vector<std::unique_ptr<DiagonalizerInterface<double>>> diagonalizers;
     346           1 :     diagonalizers.push_back(std::make_unique<DiagonalizerEigen<double>>());
     347             : #ifdef WITH_LAPACKE
     348           1 :     diagonalizers.push_back(std::make_unique<DiagonalizerLapackeEvd<double>>());
     349             : #endif
     350             : 
     351           3 :     for (const auto &diagonalizer : diagonalizers) {
     352           2 :         auto system = SystemAtom<double>(basis);
     353           2 :         system.set_electric_field(
     354             :             {1 * VOLT_PER_CM_IN_ATOMIC_UNITS, 0, 1 * VOLT_PER_CM_IN_ATOMIC_UNITS});
     355             : 
     356           2 :         system.diagonalize(*diagonalizer, min_energy, max_energy, 1e-6);
     357           2 :         auto eigenenergies_pairinteraction = system.get_eigenenergies();
     358             : 
     359           2 :         DOCTEST_CHECK(eigenenergies_pairinteraction.size() == 0);
     360           2 :     }
     361           1 : }
     362             : 
     363           1 : DOCTEST_TEST_CASE("atom ion interaction") {
     364           1 :     auto &database = Database::get_global_instance();
     365           1 :     DiagonalizerEigen<double> diagonalizer;
     366             : 
     367           0 :     auto ket = KetAtomCreator()
     368           2 :                    .set_species("Rb")
     369           1 :                    .set_quantum_number_n(60)
     370           1 :                    .set_quantum_number_l(1)
     371           1 :                    .set_quantum_number_j(0.5)
     372           1 :                    .set_quantum_number_m(0.5)
     373           1 :                    .create(database);
     374           1 :     double energy = ket->get_energy();
     375           1 :     double min_energy = energy - 50 / HARTREE_IN_GHZ;
     376           1 :     double max_energy = energy + 50 / HARTREE_IN_GHZ;
     377             : 
     378           0 :     auto basis = BasisAtomCreator<double>()
     379           2 :                      .set_species("Rb")
     380           1 :                      .restrict_quantum_number_n(58, 62)
     381           1 :                      .restrict_quantum_number_l(0, 3)
     382           1 :                      .restrict_quantum_number_m(0.5, 0.5)
     383           1 :                      .create(database);
     384             : 
     385           1 :     auto system3 = SystemAtom<double>(basis);
     386           1 :     system3.set_ion_interaction_order(3);
     387           1 :     system3.set_ion_distance_vector({0, 0, 10 * UM_IN_ATOMIC_UNITS});
     388           1 :     system3.diagonalize(diagonalizer, min_energy, max_energy, 1e-6);
     389           1 :     auto energies3 = system3.get_eigenenergies();
     390             : 
     391           1 :     auto system2 = SystemAtom<double>(basis);
     392           1 :     system2.set_ion_interaction_order(2);
     393           1 :     system2.set_ion_distance_vector({0, 0, 10 * UM_IN_ATOMIC_UNITS});
     394           1 :     system2.diagonalize(diagonalizer, min_energy, max_energy, 1e-6);
     395           1 :     auto energies2 = system2.get_eigenenergies();
     396             : 
     397             :     // Ensure that the quadrupole order has a significant effect
     398           1 :     size_t num_energies = std::min(energies2.size(), energies3.size());
     399          17 :     for (size_t i = 0; i < num_energies; ++i) {
     400          16 :         DOCTEST_CHECK(std::abs(energies3[i] - energies2[i]) * HARTREE_IN_GHZ > 1e-6);
     401             :     }
     402           1 : }
     403             : 
     404             : } // namespace pairinteraction

Generated by: LCOV version 1.16