diff --git a/source/source_pw/module_dfpt/test_serial/CMakeLists.txt b/source/source_pw/module_dfpt/test_serial/CMakeLists.txt index 9c181c46b84..906b63bb92c 100644 --- a/source/source_pw/module_dfpt/test_serial/CMakeLists.txt +++ b/source/source_pw/module_dfpt/test_serial/CMakeLists.txt @@ -43,7 +43,7 @@ addtest( # (see test/dfpt_test_mocks.cpp). ../test/dfpt_test_mocks.cpp # Shared serial-side cell/basis/data fixture. - dfpt_serial_fixture.cpp) + dfpt_test_tool.cpp) addtest( TARGET MODULE_DFPT_rho_serial @@ -63,7 +63,7 @@ addtest( # (see test/dfpt_test_mocks.cpp). ../test/dfpt_test_mocks.cpp # Shared serial-side cell/basis/data fixture. - dfpt_serial_fixture.cpp) + dfpt_test_tool.cpp) addtest( TARGET MODULE_DFPT_phon_serial @@ -92,7 +92,7 @@ addtest( # (see test/dfpt_test_mocks.cpp). ../test/dfpt_test_mocks.cpp # Shared serial-side cell/basis/data fixture. - dfpt_serial_fixture.cpp) + dfpt_test_tool.cpp) addtest( TARGET MODULE_DFPT_q0_serial @@ -114,7 +114,7 @@ addtest( # (see test/dfpt_test_mocks.cpp). ../test/dfpt_test_mocks.cpp # Shared serial-side cell/basis/data fixture. - dfpt_serial_fixture.cpp) + dfpt_test_tool.cpp) # QE-format dyn writer + the Gamma-first q-ordering validation consumed by # the DFPT driver (test_dfpt_write_dyn.cpp matches dfpt_write_dyn.cpp, @@ -133,7 +133,7 @@ addtest( # (see test/dfpt_test_mocks.cpp). ../test/dfpt_test_mocks.cpp # Shared serial-side cell/basis/data fixture. - dfpt_serial_fixture.cpp) + dfpt_test_tool.cpp) # Analytic libxc first-order XC kernel (empty TU without libxc, so the test # only exists when the feature is compiled in) @@ -156,5 +156,5 @@ if(ENABLE_LIBXC) # (see test/dfpt_test_mocks.cpp). ../test/dfpt_test_mocks.cpp # Shared serial-side cell/basis/data fixture. - dfpt_serial_fixture.cpp) + dfpt_test_tool.cpp) endif() diff --git a/source/source_pw/module_dfpt/test_serial/dfpt_pert_serial_test.cpp b/source/source_pw/module_dfpt/test_serial/dfpt_pert_serial_test.cpp index a50fa7e9613..76a0aa32142 100644 --- a/source/source_pw/module_dfpt/test_serial/dfpt_pert_serial_test.cpp +++ b/source/source_pw/module_dfpt/test_serial/dfpt_pert_serial_test.cpp @@ -11,14 +11,17 @@ // the real serial initgrids/initparameters/setuptransform path on a shared // FFT grid, exactly like the production setup_pwrho/setup_pwwfc sequence. -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/constants.h" #include "source_base/matrix3.h" #include "source_base/vector3.h" #include "source_cell/atom_pseudo.h" #include "source_cell/atom_spec.h" -#include "source_cell/qlist.h" -#include "source_cell/unitcell.h" #include "source_psi/psi.h" #include "source_pw/module_dfpt/dfpt_pert.h" #include "source_cell/stru_fac.h" @@ -49,17 +52,38 @@ * - build_dv under with_u()/u_active()==false (pure-PW DFT+U safety). */ -class DFPTPertSerialTest : public DFPTSerialBase +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTPertSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; + // default (k, q): q generic, k = -q so k+q = 0; tau of atom 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + const ModuleBase::Vector3 tau_{1.1, 2.3, 0.7}; + void SetUp() override { - DFPTSerialBase::SetUp(); + dfpt_test::setup_cell(ucell_, latvec_); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d_, q_d_, 2, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); } + + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } }; TEST_F(DFPTPertSerialTest, RhoGvecMatchesDistributedGcar) @@ -100,8 +124,8 @@ TEST_F(DFPTPertSerialTest, DVlocDtauMatchesFiniteDifference) // per bohr of displacement (GS stru_fac phase convention) const double ap = -ModuleBase::TWO_PI * (w * (tau_ + eps * d)); const double am = -ModuleBase::TWO_PI * (w * (tau_ - eps * d)); - const std::complex fd = VlocCoulomb((w * w) * ucell_.tpiba2) - * (std::polar(1.0, ap) - std::polar(1.0, am)) / (2.0 * eps * lat0_); + const std::complex fd = dfpt_test::vloc_coulomb(ucell_.atoms[0].ncpp.zv, ucell_.omega, (w * w) * ucell_.tpiba2) + * (std::polar(1.0, ap) - std::polar(1.0, am)) / (2.0 * eps * dfpt_test::lat0); EXPECT_NEAR(dv[ig].real(), fd.real(), 1.0e-9); EXPECT_NEAR(dv[ig].imag(), fd.imag(), 1.0e-9); } @@ -120,7 +144,7 @@ TEST_F(DFPTPertSerialTest, ApplyDvConvolutionMatchesAnalyticMatrixElement) ASSERT_EQ(dv_recip.size(), static_cast(pw_rho_.npw)); for (int ig = 0; ig < pw_rho_.npw; ++ig) { - const std::complex expect = AnalyticDVloc(0, pw_rho_.gcar[ig] + q_cart_); + const std::complex expect = dfpt_test::analytic_dvloc(ucell_, tau_, 0, pw_rho_.gcar[ig] + q_cart_); EXPECT_NEAR(dv_recip[ig].real(), expect.real(), 1.0e-9); EXPECT_NEAR(dv_recip[ig].imag(), expect.imag(), 1.0e-9); } @@ -131,16 +155,16 @@ TEST_F(DFPTPertSerialTest, ApplyDvConvolutionMatchesAnalyticMatrixElement) int ig_zero = -1, ig_x = -1, ig_y = -1; for (int ig = 0; ig < npwk; ++ig) { - const long long key = GKey(pw_wfc_.getgpluskcar(0, ig) - kc); - if (key == FKey(0, 0, 0)) + const long long key = dfpt_test::gkey(pw_wfc_.getgpluskcar(0, ig) - kc, dfpt_test::cubic_edge); + if (key == dfpt_test::fkey(0, 0, 0)) { ig_zero = ig; } - else if (key == FKey(1, 0, 0)) + else if (key == dfpt_test::fkey(1, 0, 0)) { ig_x = ig; } - else if (key == FKey(0, 1, 0)) + else if (key == dfpt_test::fkey(0, 1, 0)) { ig_y = ig; } @@ -168,9 +192,9 @@ TEST_F(DFPTPertSerialTest, ApplyDvConvolutionMatchesAnalyticMatrixElement) for (int igl = 0; igl < kq.get_npwk(); ++igl) { const ModuleBase::Vector3 gpp = kq.get_gcar(igl); - const std::complex e0 = AnalyticDVloc(0, gpp + q_cart_); - const std::complex e1 = 0.7 * AnalyticDVloc(0, gpp - g1 + q_cart_) - + std::complex(0.3, 0.2) * AnalyticDVloc(0, gpp - g2 + q_cart_); + const std::complex e0 = dfpt_test::analytic_dvloc(ucell_, tau_, 0, gpp + q_cart_); + const std::complex e1 = 0.7 * dfpt_test::analytic_dvloc(ucell_, tau_, 0, gpp - g1 + q_cart_) + + std::complex(0.3, 0.2) * dfpt_test::analytic_dvloc(ucell_, tau_, 0, gpp - g2 + q_cart_); EXPECT_NEAR(d0[igl].real(), e0.real(), 1.0e-8); EXPECT_NEAR(d0[igl].imag(), e0.imag(), 1.0e-8); EXPECT_NEAR(d1[igl].real(), e1.real(), 1.0e-8); @@ -180,7 +204,7 @@ TEST_F(DFPTPertSerialTest, ApplyDvConvolutionMatchesAnalyticMatrixElement) TEST_F(DFPTPertSerialTest, BuildVkbL0MatchesIndependentSimpson) { - MakeNCAtom(); + dfpt_test::make_nc_atom(ucell_); const int npwk = pw_wfc_.npwk[0]; std::vector> gk(npwk); for (int ig = 0; ig < npwk; ++ig) @@ -245,7 +269,7 @@ TEST_F(DFPTPertSerialTest, BuildVkbL0MatchesIndependentSimpson) TEST_F(DFPTPertSerialTest, DVnlDtauMatchesOperatorFiniteDifference) { - MakeNCAtom(); + dfpt_test::make_nc_atom(ucell_); const int npwk = pw_wfc_.npwk[0]; ModuleDFPT::DFPT_KQ_Basis kq; kq.init(&pw_wfc_, &pw_rho_, q_cart_, 0); @@ -354,7 +378,7 @@ TEST_F(DFPTPertSerialTest, DVnlDtauMatchesOperatorFiniteDifference) } for (int igl = 0; igl < npwkq; ++igl) { - fd[igl] /= (2.0 * eps * lat0_); + fd[igl] /= (2.0 * eps * dfpt_test::lat0); EXPECT_NEAR(dv_psi[b][igl].real(), fd[igl].real(), 1.0e-7); EXPECT_NEAR(dv_psi[b][igl].imag(), fd[igl].imag(), 1.0e-7); } @@ -364,7 +388,7 @@ TEST_F(DFPTPertSerialTest, DVnlDtauMatchesOperatorFiniteDifference) TEST_F(DFPTPertSerialTest, NonlocalPathRejectsUltrasoft) { ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - MakeNCAtom(); + dfpt_test::make_nc_atom(ucell_); ucell_.atoms[0].ncpp.tvanp = true; const int npwk = pw_wfc_.npwk[0]; psi::Psi> psi(1, 1, npwk, npwk, true); diff --git a/source/source_pw/module_dfpt/test_serial/dfpt_serial_fixture.cpp b/source/source_pw/module_dfpt/test_serial/dfpt_serial_fixture.cpp deleted file mode 100644 index aa8ed639268..00000000000 --- a/source/source_pw/module_dfpt/test_serial/dfpt_serial_fixture.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "dfpt_serial_fixture.h" - -#include "source_base/constants.h" - -#include - -void DFPTSerialBase::SetUp() -{ - SetUpCell(); - SetupBases(k_d_, q_d_, 2); -} - -void DFPTSerialBase::SetUpCell() -{ - latvec_ = ModuleBase::Matrix3(a_, 0.0, 0.0, 0.0, a_, 0.0, 0.0, 0.0, a_); - ucell_.ntype = 1; - ucell_.nat = 1; - ucell_.atoms = new Atom[1]; - ucell_.atoms[0].na = 1; - ucell_.atoms[0].tau.resize(1); - ucell_.atoms[0].tau[0] = tau_; - ucell_.latvec = latvec_; - ucell_.GT = latvec_.Inverse(); - ucell_.G = ucell_.GT.Transpose(); - ucell_.lat0 = lat0_; - ucell_.tpiba = ModuleBase::TWO_PI / lat0_; - ucell_.tpiba2 = ucell_.tpiba * ucell_.tpiba; - ucell_.omega = a_ * a_ * a_ * lat0_ * lat0_ * lat0_; - ucell_.iat2it.resize(1); - ucell_.iat2ia.resize(1); - ucell_.iat2it[0] = 0; - ucell_.iat2ia[0] = 0; - MakeCoulombAtom(); -} - -void DFPTSerialBase::SetupBases(const ModuleBase::Vector3& k_d, - const ModuleBase::Vector3& q_d, - int nbands) -{ - G_ = ucell_.G; - pw_rho_.initgrids(lat0_, latvec_, rho_mult_ * ecutwfc_); - pw_rho_.initparameters(false, rho_mult_ * ecutwfc_); - pw_rho_.fft_bundle.initfftmode(0); - pw_rho_.setuptransform(); - pw_rho_.collect_local_pw(); - - const ModuleBase::Vector3 klist[1] = {k_d}; - pw_wfc_.initgrids(lat0_, latvec_, pw_rho_.nx, pw_rho_.ny, pw_rho_.nz); - pw_wfc_.initparameters(false, ecutwfc_, 1, klist); - pw_wfc_.fft_bundle.initfftmode(0); - pw_wfc_.setuptransform(); - pw_wfc_.collect_local_pw(); - - qlist_.nkstot = 1; - qlist_.kvec_d.clear(); - qlist_.kvec_d.push_back(q_d); - q_cart_ = q_d * ucell_.G; - - data_.init(&qlist_, 1, nbands, pw_wfc_.npwk_max, pw_rho_.nrxx, 1, 1, nullptr); -} - -void DFPTSerialBase::TearDown() -{ - delete[] ucell_.atoms; - ucell_.atoms = nullptr; -} - -void DFPTSerialBase::MakeCoulombAtom() -{ - Atom& at = ucell_.atoms[0]; - at.label = "C"; - at.coulomb_potential = true; - at.ncpp.zv = 4.0; - at.ncpp.tvanp = false; - at.ncpp.has_so = false; - at.ncpp.nbeta = 0; - at.ncpp.nh = 0; - at.ncpp.msh = 0; - at.ncpp.kkbeta = 0; - at.mass = 12.0; -} - -void DFPTSerialBase::MakeNCAtom() -{ - Atom& at = ucell_.atoms[0]; - at.label = "Si"; - at.coulomb_potential = false; - pseudo& p = at.ncpp; - p.zv = 4.0; - p.tvanp = false; - p.has_so = false; - p.nbeta = 2; - p.lll = {0, 1}; - p.nh = 4; - p.msh = 121; - p.kkbeta = 121; - p.r.resize(121); - p.rab.resize(121); - p.vloc_at.assign(121, 0.0); - const double dx = 0.025; - for (int i = 0; i < 121; ++i) - { - p.r[i] = i * dx; - p.rab[i] = dx; - } - p.betar.create(2, 121); - for (int i = 0; i < 121; ++i) - { - const double r = p.r[i]; - p.betar(0, i) = std::exp(-std::pow(r - 1.0, 2) / (2.0 * 0.3 * 0.3)); - p.betar(1, i) = std::exp(-std::pow(r - 1.2, 2) / (2.0 * 0.35 * 0.35)); - } - p.dion.create(2, 2); - p.dion(0, 0) = 0.8; - p.dion(0, 1) = 0.15; - p.dion(1, 0) = -0.25; - p.dion(1, 1) = 1.1; -} - -void DFPTSerialBase::MakeTwoAtomCell() -{ - ucell_.ntype = 2; - ucell_.nat = 2; - delete[] ucell_.atoms; - ucell_.atoms = new Atom[2]; - ucell_.atoms[0].na = 1; - ucell_.atoms[1].na = 1; - ucell_.atoms[0].tau.resize(1); - ucell_.atoms[1].tau.resize(1); - ucell_.atoms[0].tau[0] = ModuleBase::Vector3(0.0, 0.0, 0.0); - ucell_.atoms[1].tau[0] = ModuleBase::Vector3(0.25, 0.31, 0.17); - for (int it = 0; it < 2; ++it) - { - Atom& at = ucell_.atoms[it]; - at.label = (it == 0) ? "A" : "B"; - at.coulomb_potential = true; - at.ncpp.zv = (it == 0) ? 4.0 : 2.0; - at.ncpp.tvanp = false; - at.ncpp.has_so = false; - at.ncpp.nbeta = 0; - at.ncpp.nh = 0; - at.mass = (it == 0) ? 12.0 : 4.0; - } - ucell_.iat2it.resize(2); - ucell_.iat2ia.resize(2); - ucell_.iat2it[0] = 0; - ucell_.iat2ia[0] = 0; - ucell_.iat2it[1] = 1; - ucell_.iat2ia[1] = 0; -} - -void DFPTSerialBase::MakeNlccTwoAtomCell() -{ - // start from the symmetric-broken two-atom cell, then upgrade the - // second species (the one at the non-origin tau_nlcc_) to an NC - // pseudo with a nonlinear core correction - MakeTwoAtomCell(); - Atom& at = ucell_.atoms[1]; - at.label = "Bc"; - at.coulomb_potential = false; - pseudo& p = at.ncpp; - p.zv = 2.0; - p.tvanp = false; - p.has_so = false; - p.nbeta = 0; - p.lll = {}; - p.nh = 0; - p.msh = 121; - p.kkbeta = 121; - p.r.resize(121); - p.rab.resize(121); - p.vloc_at.assign(121, 0.0); - const double dx = 0.025; - for (int i = 0; i < 121; ++i) - { - p.r[i] = i * dx; - p.rab[i] = dx; - } - // Gaussian core charge normalized to the integrated nlcc_core_charge_ - // (rho_atc carries the 4 pi r^2 measure of the radial grid) - const double s2 = nlcc_sigma_ * nlcc_sigma_; - double norm = 0.0; - for (int i = 0; i < 121; ++i) - { - const double r = p.r[i]; - norm += r * r * std::exp(-r * r / (2.0 * s2)) * p.rab[i]; - } - const double amp = nlcc_core_charge_ / (norm * ModuleBase::FOUR_PI); - p.rho_atc.resize(121); - for (int i = 0; i < 121; ++i) - { - const double r = p.r[i]; - p.rho_atc[i] = amp * std::exp(-r * r / (2.0 * s2)); - } - p.nlcc = true; -} - -long long DFPTSerialBase::FKey(int ix, int iy, int iz) const -{ - return (static_cast(ix + 64) * 128 + (iy + 64)) * 128 + (iz + 64); -} - -long long DFPTSerialBase::GKey(const ModuleBase::Vector3& g) const -{ - return FKey(static_cast(std::llround(g.x * a_)), - static_cast(std::llround(g.y * a_)), - static_cast(std::llround(g.z * a_))); -} - -double DFPTSerialBase::VlocCoulomb(double g2_bohr) const -{ - return -ucell_.atoms[0].ncpp.zv * ModuleBase::e2 * ModuleBase::FOUR_PI / ucell_.omega / g2_bohr; -} - -std::complex DFPTSerialBase::AnalyticDVloc(int dir, const ModuleBase::Vector3& w) const -{ - const double w2 = w * w; - if (w2 < 1.0e-12) - { - return std::complex(0.0, 0.0); - } - const double arg = -ModuleBase::TWO_PI * (w * tau_); - return std::complex(0.0, -1.0) * (ucell_.tpiba * w[dir]) * VlocCoulomb(w2 * ucell_.tpiba2) - * std::complex(std::cos(arg), std::sin(arg)); -} diff --git a/source/source_pw/module_dfpt/test_serial/dfpt_serial_fixture.h b/source/source_pw/module_dfpt/test_serial/dfpt_serial_fixture.h deleted file mode 100644 index 065ee223fd2..00000000000 --- a/source/source_pw/module_dfpt/test_serial/dfpt_serial_fixture.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef DFPT_SERIAL_FIXTURE_H -#define DFPT_SERIAL_FIXTURE_H - -#include "source_base/matrix3.h" -#include "source_base/vector3.h" -#include "source_basis/module_pw/pw_basis.h" -#include "source_basis/module_pw/pw_basis_k.h" -#include "source_cell/qlist.h" -#include "source_cell/unitcell.h" -#include "source_pw/module_dfpt/dfpt_pw_data.h" - -#include "gtest/gtest.h" -#include - -// Shared serial-side gtest fixture for the DFPT unit tests -// (dfpt_pert/rho/phon/q0_serial_test.cpp). Everything runs without -// __MPI: the plane-wave bases are built through the real serial -// initgrids/initparameters/setuptransform path on a shared FFT grid, -// exactly like the production setup_pwrho/setup_pwwfc sequence. -// -// All members the fixture touches (UnitCell geometry fields, the -// Atom/pseudo public data, QList::nkstot / kvec_d and the -// DFPT_PW_Data::init entry) are public, so the tests include the -// cell/qlist/dfpt headers normally. - -class DFPTSerialBase : public testing::Test -{ - protected: - const double lat0_ = 1.8897261254578281; - const double ecutwfc_ = 2.5; // Ry - // rho cutoff inflated to 9x ecutwfc so every Delta = G''-G' of the - // convolution lies inside the rho ball and nothing aliases - const double rho_mult_ = 9.0; - const double a_ = 10.0; // cubic edge in lat0 units - - ModuleBase::Matrix3 latvec_; - UnitCell ucell_; - ModulePW::PW_Basis pw_rho_; - ModulePW::PW_Basis_K pw_wfc_; - ModuleCell::QList qlist_; - ModuleDFPT::DFPT_PW_Data data_; - ModuleBase::Matrix3 G_; // = latvec^-T, the reciprocal builder - - // default (k, q) of the pert/phon/rho fixtures: q is generic and - // k = -q so k+q = 0: the k+q ball then stays inside the ground-state - // G list (single-k limitation documented in DFPT_KQ_Basis) - const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; - const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; - ModuleBase::Vector3 q_cart_; - const ModuleBase::Vector3 tau_{1.1, 2.3, 0.7}; // lat0 units - - // single-atom Coulomb cell + shared-grid bases at the default (k_d_, q_d_) - void SetUp() override; - void TearDown() override; - - // cubic single-atom Coulomb cell (iat2it/iat2ia allocated) - void SetUpCell(); - - // (re)initialize the bases and the shared data wiring for a given - // (k, q) pair and band count; SetUp uses the default fixture values - void SetupBases(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d, int nbands); - - void MakeCoulombAtom(); - void MakeNCAtom(); - - /// extend the two-atom cell of MakeTwoAtomCell with a nonlinear core - /// correction on the second species: a Gaussian rho_atc of total charge - /// nlcc_core_charge_ on the existing radial grid (msh points). The - /// atom sits at the non-origin tau_nlcc_ = (0.25, 0.31, 0.17) -- the - /// anti-blind-spot position: every structure-factor phase is active, - /// so a mirrored phase sign (exp(+iG.tau) instead of the QE addcore - /// exp(-iG.tau)) places the response at the wrong site and is caught - /// directly by the drhoc-localization test. - void MakeNlccTwoAtomCell(); - - /// total integrated core charge (in e) of the nlcc Gaussian above - static constexpr double nlcc_core_charge_ = 0.24; - const ModuleBase::Vector3 tau_nlcc_{0.25, 0.31, 0.17}; // lat0 units - double nlcc_sigma_ = 1.5; // bohr, grid-resolvable (spacing ~0.63 bohr) - - // reconfigure the cell as a two-atom Z=4/Z=2 crystal breaking all symmetry - void MakeTwoAtomCell(); - - // key of an integer FFT triple (gcar * a is integral on the cubic cell) - long long FKey(int ix, int iy, int iz) const; - long long GKey(const ModuleBase::Vector3& g) const; - - // analytic Coulomb local potential (Ry) at |g|^2 in bohr^-2, mirroring - // vl_pw.cpp::vloc_coulomb independently of DFPT_Pert::vloc_at_g - double VlocCoulomb(double g2_bohr) const; - - // analytic dVloc/dtau_alpha coefficient at displacement vector w (1/lat0); - // GS structure-factor convention (stru_fac.cpp): exp(-i 2pi (g.tau)) and - // dV/dtau = -i (Delta+q)_alpha tpiba Vloc exp(-i 2pi (Delta+q).tau) - std::complex AnalyticDVloc(int dir, const ModuleBase::Vector3& w) const; -}; - -#endif // DFPT_SERIAL_FIXTURE_H diff --git a/source/source_pw/module_dfpt/test_serial/dfpt_test_tool.cpp b/source/source_pw/module_dfpt/test_serial/dfpt_test_tool.cpp new file mode 100644 index 00000000000..b920996ecd9 --- /dev/null +++ b/source/source_pw/module_dfpt/test_serial/dfpt_test_tool.cpp @@ -0,0 +1,259 @@ +// Shared free-function utilities for the DFPT serial unit tests, +// implementation (see dfpt_test_tool.h). Everything runs without __MPI: +// the plane-wave bases are built through the real serial +// initgrids/initparameters/setuptransform path on a shared FFT grid, +// exactly like the production setup_pwrho/setup_pwwfc sequence. + +#include "dfpt_test_tool.h" + +#include "source_base/constants.h" +#include "source_base/matrix3.h" +#include "source_base/vector3.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/atom_pseudo.h" +#include "source_cell/atom_spec.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_psi/psi.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" + +#include +#include + +namespace dfpt_test +{ + +// nlcc Gaussian conventions of make_nlcc_two_atom_cell +constexpr double nlcc_sigma = 1.5; // bohr, grid-resolvable (spacing ~0.63 bohr) +constexpr double nlcc_core_charge = 0.24; // e, integrated + +void setup_cell(UnitCell& ucell, ModuleBase::Matrix3& latvec) +{ + latvec = ModuleBase::Matrix3(cubic_edge, 0.0, 0.0, 0.0, cubic_edge, 0.0, 0.0, 0.0, cubic_edge); + ucell.ntype = 1; + ucell.nat = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].na = 1; + ucell.atoms[0].tau.resize(1); + ucell.atoms[0].tau[0] = ModuleBase::Vector3(1.1, 2.3, 0.7); + ucell.latvec = latvec; + ucell.GT = latvec.Inverse(); + ucell.G = ucell.GT.Transpose(); + ucell.lat0 = lat0; + ucell.tpiba = ModuleBase::TWO_PI / lat0; + ucell.tpiba2 = ucell.tpiba * ucell.tpiba; + ucell.omega = cubic_edge * cubic_edge * cubic_edge * lat0 * lat0 * lat0; + ucell.iat2it.resize(1); + ucell.iat2ia.resize(1); + ucell.iat2it[0] = 0; + ucell.iat2ia[0] = 0; + make_coulomb_atom(ucell); +} + +void make_coulomb_atom(UnitCell& ucell) +{ + Atom& at = ucell.atoms[0]; + at.label = "C"; + at.coulomb_potential = true; + at.ncpp.zv = 4.0; + at.ncpp.tvanp = false; + at.ncpp.has_so = false; + at.ncpp.nbeta = 0; + at.ncpp.nh = 0; + at.ncpp.msh = 0; + at.ncpp.kkbeta = 0; + at.mass = 12.0; +} + +void make_nc_atom(UnitCell& ucell) +{ + Atom& at = ucell.atoms[0]; + at.label = "Si"; + at.coulomb_potential = false; + pseudo& p = at.ncpp; + p.zv = 4.0; + p.tvanp = false; + p.has_so = false; + p.nbeta = 2; + p.lll = {0, 1}; + p.nh = 4; + p.msh = 121; + p.kkbeta = 121; + p.r.resize(121); + p.rab.resize(121); + p.vloc_at.assign(121, 0.0); + const double dx = 0.025; + for (int i = 0; i < 121; ++i) + { + p.r[i] = i * dx; + p.rab[i] = dx; + } + p.betar.create(2, 121); + for (int i = 0; i < 121; ++i) + { + const double r = p.r[i]; + p.betar(0, i) = std::exp(-std::pow(r - 1.0, 2) / (2.0 * 0.3 * 0.3)); + p.betar(1, i) = std::exp(-std::pow(r - 1.2, 2) / (2.0 * 0.35 * 0.35)); + } + p.dion.create(2, 2); + p.dion(0, 0) = 0.8; + p.dion(0, 1) = 0.15; + p.dion(1, 0) = -0.25; + p.dion(1, 1) = 1.1; +} + +void make_two_atom_cell(UnitCell& ucell) +{ + ucell.ntype = 2; + ucell.nat = 2; + delete[] ucell.atoms; + ucell.atoms = new Atom[2]; + ucell.atoms[0].na = 1; + ucell.atoms[1].na = 1; + ucell.atoms[0].tau.resize(1); + ucell.atoms[1].tau.resize(1); + ucell.atoms[0].tau[0] = ModuleBase::Vector3(0.0, 0.0, 0.0); + ucell.atoms[1].tau[0] = ModuleBase::Vector3(0.25, 0.31, 0.17); + for (int it = 0; it < 2; ++it) + { + Atom& at = ucell.atoms[it]; + at.label = (it == 0) ? "A" : "B"; + at.coulomb_potential = true; + at.ncpp.zv = (it == 0) ? 4.0 : 2.0; + at.ncpp.tvanp = false; + at.ncpp.has_so = false; + at.ncpp.nbeta = 0; + at.ncpp.nh = 0; + at.mass = (it == 0) ? 12.0 : 4.0; + } + ucell.iat2it.resize(2); + ucell.iat2ia.resize(2); + ucell.iat2it[0] = 0; + ucell.iat2ia[0] = 0; + ucell.iat2it[1] = 1; + ucell.iat2ia[1] = 0; +} + +void make_nlcc_two_atom_cell(UnitCell& ucell) +{ + // start from the symmetry-broken two-atom cell, then upgrade the + // second species (the one at the non-origin tau) to an NC pseudo + // with a nonlinear core correction + make_two_atom_cell(ucell); + Atom& at = ucell.atoms[1]; + at.label = "Bc"; + at.coulomb_potential = false; + pseudo& p = at.ncpp; + p.zv = 2.0; + p.tvanp = false; + p.has_so = false; + p.nbeta = 0; + p.lll = {}; + p.nh = 0; + p.msh = 121; + p.kkbeta = 121; + p.r.resize(121); + p.rab.resize(121); + p.vloc_at.assign(121, 0.0); + const double dx = 0.025; + for (int i = 0; i < 121; ++i) + { + p.r[i] = i * dx; + p.rab[i] = dx; + } + // Gaussian core charge normalized to the integrated nlcc_core_charge + // (rho_atc carries the 4 pi r^2 measure of the radial grid) + const double s2 = nlcc_sigma * nlcc_sigma; + double norm = 0.0; + for (int i = 0; i < 121; ++i) + { + const double r = p.r[i]; + norm += r * r * std::exp(-r * r / (2.0 * s2)) * p.rab[i]; + } + const double amp = nlcc_core_charge / (norm * ModuleBase::FOUR_PI); + p.rho_atc.resize(121); + for (int i = 0; i < 121; ++i) + { + const double r = p.r[i]; + p.rho_atc[i] = amp * std::exp(-r * r / (2.0 * s2)); + } + p.nlcc = true; +} + +void setup_bases(ModulePW::PW_Basis& pw_rho, + ModulePW::PW_Basis_K& pw_wfc, + ModuleCell::QList& qlist, + ModuleDFPT::DFPT_PW_Data& data, + const UnitCell& ucell, + const ModuleBase::Vector3& k_d, + const ModuleBase::Vector3& q_d, + int nbands, + ModuleBase::Vector3& q_cart) +{ + // The tool keeps its own five-call assembly chains instead of + // calling the production pw::setup_pwrho / setup_pwwfc wrappers + // (which also run without __MPI): the tests need rho_mult = 9x + // ecutwfc for the DFPT convolution (every Delta = G''-G' must fit + // in the rho ball) while production hardcodes 4x ecutwfc; they + // re-assemble for arbitrary (k, q) pairs per case, which the + // K_Vectors-driven wrapper is not shaped for; and the wrappers + // write to the running-log global stream and require a fabricated + // Input_para. + const ModuleBase::Matrix3& latvec = ucell.latvec; + pw_rho.initgrids(lat0, latvec, rho_mult * ecutwfc); + pw_rho.initparameters(false, rho_mult * ecutwfc); + pw_rho.fft_bundle.initfftmode(0); + pw_rho.setuptransform(); + pw_rho.collect_local_pw(); + + const ModuleBase::Vector3 klist[1] = {k_d}; + pw_wfc.initgrids(lat0, latvec, pw_rho.nx, pw_rho.ny, pw_rho.nz); + pw_wfc.initparameters(false, ecutwfc, 1, klist); + pw_wfc.fft_bundle.initfftmode(0); + pw_wfc.setuptransform(); + pw_wfc.collect_local_pw(); + + qlist.nkstot = 1; + qlist.kvec_d.clear(); + qlist.kvec_d.push_back(q_d); + q_cart = q_d * ucell.G; + + data.init(&qlist, 1, nbands, pw_wfc.npwk_max, pw_rho.nrxx, 1, 1, nullptr); +} + +double vloc_coulomb(double zv, double omega, double g2_bohr) +{ + return -zv * ModuleBase::e2 * ModuleBase::FOUR_PI / omega / g2_bohr; +} + +std::complex analytic_dvloc(const UnitCell& ucell, + const ModuleBase::Vector3& tau, + int dir, + const ModuleBase::Vector3& w) +{ + const double w2 = w * w; + if (w2 < 1.0e-12) + { + return std::complex(0.0, 0.0); + } + const double zv = ucell.atoms[0].ncpp.zv; + const double arg = -ModuleBase::TWO_PI * (w * tau); + return std::complex(0.0, -1.0) * (ucell.tpiba * w[dir]) + * vloc_coulomb(zv, ucell.omega, w2 * ucell.tpiba2) + * std::complex(std::cos(arg), std::sin(arg)); +} + +long long fkey(int ix, int iy, int iz) +{ + return (static_cast(ix + 64) * 128 + (iy + 64)) * 128 + (iz + 64); +} + +long long gkey(const ModuleBase::Vector3& g, double a) +{ + return fkey(static_cast(std::llround(g.x * a)), + static_cast(std::llround(g.y * a)), + static_cast(std::llround(g.z * a))); +} + +} // namespace dfpt_test diff --git a/source/source_pw/module_dfpt/test_serial/dfpt_test_tool.h b/source/source_pw/module_dfpt/test_serial/dfpt_test_tool.h new file mode 100644 index 00000000000..6dd78684d93 --- /dev/null +++ b/source/source_pw/module_dfpt/test_serial/dfpt_test_tool.h @@ -0,0 +1,100 @@ +#ifndef DFPT_TEST_TOOL_H +#define DFPT_TEST_TOOL_H + +// Shared free-function utilities for the DFPT serial unit tests +// (module_pw test/test_tool.{h,cpp} pattern): every test file derives +// its own gtest fixture from ::testing::Test, owns the objects it needs +// and calls these functions -- there is no shared fixture base class. + +#include + +class UnitCell; +namespace ModuleBase +{ +class Matrix3; +template +class Vector3; +} +namespace ModulePW +{ +class PW_Basis; +class PW_Basis_K; +} +namespace ModuleCell +{ +class QList; +} +namespace ModuleDFPT +{ +class DFPT_PW_Data; +} + +namespace dfpt_test +{ + +// shared test-world conventions (the fixture values every test agrees on) +constexpr double lat0 = 1.8897261254578281; // bohr +constexpr double ecutwfc = 2.5; // Ry +// rho cutoff inflated to 9x ecutwfc so every Delta = G''-G' of the +// convolution lies inside the rho ball and nothing aliases +constexpr double rho_mult = 9.0; +constexpr double cubic_edge = 10.0; // cubic cell edge in lat0 units + +// Build the cubic single-atom cell (latvec / G / GT / tpiba / omega and +// the iat2it / iat2ia wiring; the atom itself is Coulomb flavor until a +// make_*_atom call replaces it). +void setup_cell(UnitCell& ucell, ModuleBase::Matrix3& latvec); + +// Reconfigure the cell's single atom as a bare Coulomb Z=4 atom at tau. +void make_coulomb_atom(UnitCell& ucell); + +// Reconfigure the cell's single atom as a norm-conserving pseudo atom +// (analytic Bessel projectors, no nlcc). +void make_nc_atom(UnitCell& ucell); + +// Reconfigure the cell as a two-atom Z=4/Z=2 crystal breaking all +// symmetry (masses 12/4), the second atom at the anti-blind-spot tau +// (0.25, 0.31, 0.17). +void make_two_atom_cell(UnitCell& ucell); + +// Extend the two-atom cell with a nonlinear core correction on the +// second species: a Gaussian rho_atc of total charge 0.24 e at the +// non-origin tau (0.25, 0.31, 0.17). +void make_nlcc_two_atom_cell(UnitCell& ucell); + +// Assemble the shared rho/wfc bases for a (k, q) pair and bind the q +// list and the data object: the rho basis at rho_mult * ecutwfc, the +// wfc basis on the rho FFT box with the k list, the q list holding the +// single q, and q_cart = q_frac * ucell.G. Convention: k defaults to -q +// so the k+q ball stays inside the ground-state G list. +void setup_bases(ModulePW::PW_Basis& pw_rho, + ModulePW::PW_Basis_K& pw_wfc, + ModuleCell::QList& qlist, + ModuleDFPT::DFPT_PW_Data& data, + const UnitCell& ucell, + const ModuleBase::Vector3& k_d, + const ModuleBase::Vector3& q_d, + int nbands, + ModuleBase::Vector3& q_cart); + +// Analytic Coulomb local potential (Ry) at g2_bohr (bohr^-2) for valence +// zv in cell volume omega, mirroring +// vl_pw.cpp::vloc_coulomb independently of DFPT_Pert::vloc_at_g. +double vloc_coulomb(double zv, double omega, double g2_bohr); + +// Analytic dVloc/dtau_alpha coefficient at displacement vector w +// (1/lat0): dV/dtau = -i (w)_alpha tpiba Vloc exp(-i 2pi w.tau) with the +// GS structure-factor phase convention. +std::complex analytic_dvloc(const UnitCell& ucell, + const ModuleBase::Vector3& tau, + int dir, + const ModuleBase::Vector3& w); + +// Integer FFT triple key (ix, iy, iz) on the cubic cell. +long long fkey(int ix, int iy, int iz); +// Key of g on the cubic cell (gcar * a is integral). +long long gkey(const ModuleBase::Vector3& g, double a); + +} // namespace dfpt_test + +#endif // DFPT_TEST_TOOL_H diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon.cpp index 62780636479..ca564697d05 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon.cpp @@ -3,33 +3,60 @@ // form, the Gamma check_sum_rule, the LO-TO direction normalization, the // corrected-frequency diagonalization, and the output format regression. -#include "dfpt_serial_fixture.h" -#include "source_base/constants.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/complexmatrix.h" +#include "source_base/constants.h" #include "source_cell/stru_fac.h" +#include "source_psi/psi.h" +#include "source_pw/module_dfpt/dfpt_kq_basis.h" #include "source_pw/module_dfpt/dfpt_pert.h" #include "source_pw/module_dfpt/dfpt_phon.h" -// per-file fixture (self-contained, gtest style): only the members and -// helpers this file's cases need -class DFPTPhonSerialTest : public DFPTSerialBase +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTPhonSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; ModuleDFPT::DFPT_Phon phon_; + // default (k, q) of this file: q is generic and k = -q so k+q = 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + void SetUp() override { - DFPTSerialBase::SetUp(); + dfpt_test::setup_cell(ucell_, latvec_); SetupPhon(k_d_, q_d_); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + // (re)initialize the bases and the pert/phon wiring for a given (k, q) // pair; SetUp uses the default fixture values void SetupPhon(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d) { - SetupBases(k_d, q_d, 2); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d, q_d, 2, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); phon_.init(ucell_, &pw_rho_, &pert_); } @@ -59,12 +86,9 @@ class DFPTPhonSerialTest : public DFPTSerialBase z2(0, 0) = z2(1, 1) = z2(2, 2) = 2.0; data_.set_born(0, z1); data_.set_born(1, z2); - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); } }; - -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include #include #include @@ -76,7 +100,7 @@ TEST_F(DFPTPhonSerialTest, DiagonalizeKnownMatrix) // 2-atom layout so nat3 = 6. Hermitian blocks with known closed-form // spectra: [[a, c], [conj(c), b]] has eigenvalues (a+b)/2 // +- sqrt(((a-b)/2)^2 + |c|^2) - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); const double lam[6] = {0.04, 0.09, 0.16, -0.02, 0.01, 0.1225}; // Ry/bohr^2/amu ModuleBase::ComplexMatrix dyn(6, 6, true); for (int i = 0; i < 6; ++i) diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_asr.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_asr.cpp index 41f431c6278..202c0ce5b87 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_asr.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_asr.cpp @@ -4,43 +4,68 @@ // sum rule, ASR+LO-TO interplay, and the directional-cancellation and // cross-Cartesian regressions. -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/complexmatrix.h" #include "source_cell/stru_fac.h" +#include "source_psi/psi.h" +#include "source_pw/module_dfpt/dfpt_kq_basis.h" #include "source_pw/module_dfpt/dfpt_pert.h" #include "source_pw/module_dfpt/dfpt_phon.h" -// per-file fixture (self-contained, gtest style): only the members and -// helpers this file's cases need -class DFPTPhonSerialTest : public DFPTSerialBase +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include +#include + +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTPhonSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; ModuleDFPT::DFPT_Phon phon_; + // default (k, q) of this file: q is generic and k = -q so k+q = 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + void SetUp() override { - DFPTSerialBase::SetUp(); + dfpt_test::setup_cell(ucell_, latvec_); SetupPhon(k_d_, q_d_); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + // (re)initialize the bases and the pert/phon wiring for a given (k, q) // pair; SetUp uses the default fixture values void SetupPhon(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d) { - SetupBases(k_d, q_d, 2); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d, q_d, 2, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); phon_.init(ucell_, &pw_rho_, &pert_); } }; -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include -#include -#include -#include namespace { @@ -90,7 +115,7 @@ ModuleBase::ComplexMatrix make_violating_dyn() TEST_F(DFPTPhonSerialTest, AsrSimpleZeroesRowSums) { - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); // rewire the q list to Gamma (the fixture default q is not 0) SetupPhon(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0)); const ModuleBase::ComplexMatrix dyn0 = make_violating_dyn(); @@ -113,7 +138,7 @@ TEST_F(DFPTPhonSerialTest, AsrSimpleZeroesRowSums) TEST_F(DFPTPhonSerialTest, AsrCrystalZeroesRowSumsExactlyAndProjectsAcousticToZero) { - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); // rewire the q list to Gamma (the fixture default q is not 0) SetupPhon(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0)); data_.set_dynmat(0, make_violating_dyn()); @@ -144,7 +169,7 @@ TEST_F(DFPTPhonSerialTest, AsrCrystalZeroesRowSumsExactlyAndProjectsAcousticToZe TEST_F(DFPTPhonSerialTest, AsrCrystalKeepsEquivalentAtomsEquivalent) { - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); // symmetry-equivalent atoms carry equal masses; make the two fixture // species mass-equal so the equivalence assertion is meaningful ucell_.atoms[1].mass = ucell_.atoms[0].mass; @@ -177,7 +202,7 @@ TEST_F(DFPTPhonSerialTest, AsrCrystalKeepsEquivalentAtomsEquivalent) TEST_F(DFPTPhonSerialTest, AsrCrystalCorrectsBornChargeSumRule) { - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); // rewire the q list to Gamma (the fixture default q is not 0) SetupPhon(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0)); data_.set_dynmat(0, make_violating_dyn()); @@ -211,7 +236,7 @@ TEST_F(DFPTPhonSerialTest, AsrCrystalPlusLotoKeepsAcousticExactlyZero) // the physical acoustic subspace only through sum_a Z*_a = 0 -- mixes // into the projected null space and lifts one acoustic frequency (the // bn_cubic Gamma run showed +36 cm^-1 before the fix) - MakeTwoAtomCell(); // masses 12 / 4: unequal, the trigger + dfpt_test::make_two_atom_cell(ucell_); // masses 12 / 4: unequal, the trigger SetupPhon(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0)); data_.set_dynmat(0, make_violating_dyn()); ModuleBase::matrix eps(3, 3, true); @@ -245,7 +270,7 @@ TEST_F(DFPTPhonSerialTest, AsrDirectionalCancellationDetected) // regression of the audit-metric fix: the block [[1,-1,0],[-1,1,0]] // has zero row sums (sum over ALL columns) but a NONZERO per-direction // residual D*t_x; the audit and apply_asr report must not miss it - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); SetupPhon(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0)); ModuleBase::ComplexMatrix dyn(6, 6, true); // atom0 self-block with the cancellation pattern @@ -280,7 +305,7 @@ TEST_F(DFPTPhonSerialTest, AsrSimpleCrossCartesianRegression) // Hermitian onsite-only fix; simple must still produce a final matrix // that is Hermitian, satisfies all three translation constraints // exactly, and has a real diagonal - MakeTwoAtomCell(); // masses 12/4 (unequal) + dfpt_test::make_two_atom_cell(ucell_); // masses 12/4 (unequal) SetupPhon(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0)); ModuleBase::ComplexMatrix dyn(6, 6, true); const double delta = 0.07; diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_elec.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_elec.cpp index 766544a60eb..ec9864418e9 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_elec.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_elec.cpp @@ -6,7 +6,12 @@ // = 1 keeps only the G=0 harmonic); the dpsi slot is restored after the // accumulation. Plus the nlcc kernel fill-semantics regression. -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/complexmatrix.h" #include "source_cell/stru_fac.h" #include "source_psi/psi.h" @@ -17,26 +22,46 @@ // only NlccDirectTermsFillSemantics instantiates DFPT_Rho #include "source_pw/module_dfpt/dfpt_rho.h" -// per-file fixture (self-contained, gtest style): only the members and -// helpers this file's cases need -class DFPTPhonSerialTest : public DFPTSerialBase +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTPhonSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; ModuleDFPT::DFPT_Phon phon_; + // default (k, q) of this file: q is generic and k = -q so k+q = 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + void SetUp() override { - DFPTSerialBase::SetUp(); + dfpt_test::setup_cell(ucell_, latvec_); SetupPhon(k_d_, q_d_); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + // (re)initialize the bases and the pert/phon wiring for a given (k, q) // pair; SetUp uses the default fixture values void SetupPhon(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d) { - SetupBases(k_d, q_d, 2); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d, q_d, 2, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); phon_.init(ucell_, &pw_rho_, &pert_); } @@ -85,23 +110,23 @@ class DFPTPhonSerialTest : public DFPTSerialBase const std::vector>& dpsi_inj, int adir) const { + // tau of the fixture cell's atom 0 (lat0-cartesian units) + const ModuleBase::Vector3 tau(1.1, 2.3, 0.7); std::complex cross(0.0, 0.0); for (int igl = 0; igl < kq.get_npwk(); ++igl) { const ModuleBase::Vector3 gpp = kq.get_gpluskq(igl); for (size_t ic = 0; ic < psi_coef.size(); ++ic) { - // AnalyticDVloc returns 0 at w = 0 (dVloc drop) + // dfpt_test::analytic_dvloc returns 0 at w = 0 (dVloc drop) cross += psi_coef[ic] * std::conj(dpsi_inj[igl]) - * AnalyticDVloc(adir, gpp - psi_gcart[ic] + q_cart_); + * dfpt_test::analytic_dvloc(ucell_, tau, adir, gpp - psi_gcart[ic] + q_cart_); } } return cross; } }; -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include #include #include @@ -284,9 +309,10 @@ TEST_F(DFPTPhonSerialTest, AccumulateElectronD2CommensurateQ) { continue; } - const double arg = -ModuleBase::TWO_PI * (g * tau_); + const ModuleBase::Vector3 tau(1.1, 2.3, 0.7); + const double arg = -ModuleBase::TWO_PI * (g * tau); const std::complex kterm = -(ucell_.tpiba * g[adir]) * (ucell_.tpiba * g[1]) - * VlocCoulomb(g2 * ucell_.tpiba2) + * dfpt_test::vloc_coulomb(ucell_.atoms[0].ncpp.zv, ucell_.omega, g2 * ucell_.tpiba2) * std::complex(std::cos(arg), std::sin(arg)); d2elem += std::conj(ccoef[i]) * ccoef[j] * kterm; } @@ -315,11 +341,11 @@ TEST_F(DFPTPhonSerialTest, AccumulateElectronD2CommensurateQ) // --------------------------------------------------------------------------- TEST_F(DFPTPhonSerialTest, NlccDirectTermsFillSemantics) { - MakeNlccTwoAtomCell(); + dfpt_test::make_nlcc_two_atom_cell(ucell_); SetupPhon(k_d_, q_d_); ModuleDFPT::DFPT_Rho rho; - rho.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, G_, std::string("rho_plain"), 0.4, 1.0, 8}); + rho.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, ucell_.G, std::string("rho_plain"), 0.4, 1.0, 8}); phon_.set_rho(&rho); const int nrxx = pw_rho_.nrxx; diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_ewald.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_ewald.cpp index 1b5d8aced01..6a173becff3 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_ewald.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_phon_ewald.cpp @@ -5,50 +5,75 @@ // against a direct (unscreened) lattice Hessian sum at a generic // incommensurate q where the dipole sum is oscillation-screened. -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/complexmatrix.h" #include "source_cell/stru_fac.h" +#include "source_psi/psi.h" +#include "source_pw/module_dfpt/dfpt_kq_basis.h" #include "source_pw/module_dfpt/dfpt_pert.h" #include "source_pw/module_dfpt/dfpt_phon.h" -// per-file fixture (self-contained, gtest style): only the members and -// helpers this file's cases need -class DFPTPhonSerialTest : public DFPTSerialBase +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include +#include + +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTPhonSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; ModuleDFPT::DFPT_Phon phon_; + // default (k, q) of this file: q is generic and k = -q so k+q = 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + void SetUp() override { - DFPTSerialBase::SetUp(); + dfpt_test::setup_cell(ucell_, latvec_); SetupPhon(k_d_, q_d_); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + // (re)initialize the bases and the pert/phon wiring for a given (k, q) // pair; SetUp uses the default fixture values void SetupPhon(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d) { - SetupBases(k_d, q_d, 2); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d, q_d, 2, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); phon_.init(ucell_, &pw_rho_, &pert_); } }; -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include -#include -#include -#include TEST_F(DFPTPhonSerialTest, IonIonAcousticSumRuleGamma) { // a two-atom cell with different charges/masses breaks every symmetry: // the Gamma acoustic sum rule is then a razor for the Ewald balance // (G part + real part + Gaussian self term) - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); ModuleBase::ComplexMatrix dyn(6, 6, true); phon_.ion_ion(ModuleBase::Vector3(0.0, 0.0, 0.0), dyn); @@ -86,7 +111,7 @@ TEST_F(DFPTPhonSerialTest, IonIonAcousticSumRuleGamma) TEST_F(DFPTPhonSerialTest, IonIonGammaAcousticZeroModes) { // same two-atom cell: three acoustic eigenvalues vanish at Gamma - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); ModuleBase::ComplexMatrix dyn(6, 6, true); phon_.ion_ion(ModuleBase::Vector3(0.0, 0.0, 0.0), dyn); data_.set_dynmat(0, dyn); @@ -114,7 +139,7 @@ TEST_F(DFPTPhonSerialTest, IonIonGenericQVsDirectSum) // two-atom cell at an incommensurate q: the Ewald result must agree // with a direct (unscreened) dipole-Hessian lattice sum, whose shell // oscillation e^{i q l} makes it convergent - MakeTwoAtomCell(); + dfpt_test::make_two_atom_cell(ucell_); const ModuleBase::Vector3 tau1(0.0, 0.0, 0.0); const ModuleBase::Vector3 tau2(0.25, 0.31, 0.17); const double z[2] = {4.0, 2.0}; @@ -154,9 +179,9 @@ TEST_F(DFPTPhonSerialTest, IonIonGenericQVsDirectSum) { continue; } - const ModuleBase::Vector3 r((n1 * a_ + dt.x) * lat0_, - (n2 * a_ + dt.y) * lat0_, - (n3 * a_ + dt.z) * lat0_); + const ModuleBase::Vector3 r((n1 * dfpt_test::cubic_edge + dt.x) * dfpt_test::lat0, + (n2 * dfpt_test::cubic_edge + dt.y) * dfpt_test::lat0, + (n3 * dfpt_test::cubic_edge + dt.z) * dfpt_test::lat0); const double r2 = r * r; const double r5 = r2 * r2 * std::sqrt(r2); const double ph = ModuleBase::TWO_PI * (q_d_.x * n1 + q_d_.y * n2 + q_d_.z * n3); diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_q0.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_q0.cpp index b1718be4e86..e8fa544bad1 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_q0.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_q0.cpp @@ -4,25 +4,38 @@ // closed-form screened-leg product with the dpsi-slot sentinel), and the // build_stars + rotate_tensor cyclic-group regression. -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "source_base/constants.h" #include "source_base/matrix.h" #include "source_base/matrix3.h" #include "source_base/vector3.h" #include "source_cell/atom_pseudo.h" #include "source_cell/atom_spec.h" -#include "source_cell/qlist.h" -#include "source_cell/unitcell.h" #include "source_cell/stru_fac.h" #include "source_psi/psi.h" #include "source_pw/module_dfpt/dfpt_pert.h" #include "source_pw/module_dfpt/dfpt_q0.h" -// per-file fixture (self-contained, gtest style): this fixture is -// Gamma-only (k_d_ shadows the generic default k of the base) -class DFPTQ0SerialTest : public DFPTSerialBase +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities; Gamma-only +class DFPTQ0SerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; ModuleDFPT::DFPT_Q0 q0_; @@ -32,12 +45,19 @@ class DFPTQ0SerialTest : public DFPTSerialBase void SetUp() override { - SetUpCell(); - SetupBases(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0), 4); + dfpt_test::setup_cell(ucell_, latvec_); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, + k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0), 4, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); q0_.init(ucell_, &pw_rho_, &pw_wfc_, &pert_); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + // wfc-basis index of the reciprocal vector (ix, iy, iz)/a at Gamma int IgOf(int ix, int iy, int iz) const { @@ -45,7 +65,9 @@ class DFPTQ0SerialTest : public DFPTSerialBase for (int ig = 0; ig < npwk; ++ig) { const ModuleBase::Vector3 g = pw_wfc_.getgpluskcar(0, ig); - if (std::llround(g.x * a_) == ix && std::llround(g.y * a_) == iy && std::llround(g.z * a_) == iz) + if (std::llround(g.x * dfpt_test::cubic_edge) == ix + && std::llround(g.y * dfpt_test::cubic_edge) == iy + && std::llround(g.z * dfpt_test::cubic_edge) == iz) { return ig; } @@ -54,15 +76,13 @@ class DFPTQ0SerialTest : public DFPTSerialBase } }; -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include #include #include TEST_F(DFPTQ0SerialTest, BuildVkbDkMatchesFiniteDifference) { - MakeNCAtom(); + dfpt_test::make_nc_atom(ucell_); // generic shifted list without any |g| = 0 entry (the direction chain is // singular at the origin for l >= 1 rows) const int ng = 8; @@ -270,7 +290,7 @@ TEST_F(DFPTQ0SerialTest, StarRotationCyclicGroup) for (int i = 0; i < 3; ++i) { ucell_.atoms[0].taud[i] = ModuleBase::Vector3(t0[i], t0[(i + 1) % 3], t0[(i + 2) % 3]); - ucell_.atoms[0].tau[i] = ucell_.atoms[0].taud[i] * a_; + ucell_.atoms[0].tau[i] = ucell_.atoms[0].taud[i] * dfpt_test::cubic_edge; } ucell_.iat2it.resize(3); ucell_.iat2ia.resize(3); @@ -298,8 +318,8 @@ TEST_F(DFPTQ0SerialTest, StarRotationCyclicGroup) // single reduced k point (1/4,0,0) on its own wfc basis ModulePW::PW_Basis_K kwfc; const ModuleBase::Vector3 klist[1] = {ModuleBase::Vector3(0.25, 0.0, 0.0)}; - kwfc.initgrids(lat0_, latvec_, pw_rho_.nx, pw_rho_.ny, pw_rho_.nz); - kwfc.initparameters(false, ecutwfc_, 1, klist); + kwfc.initgrids(dfpt_test::lat0, latvec_, pw_rho_.nx, pw_rho_.ny, pw_rho_.nz); + kwfc.initparameters(false, dfpt_test::ecutwfc, 1, klist); kwfc.fft_bundle.initfftmode(0); kwfc.setuptransform(); kwfc.collect_local_pw(); diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_q0_pos.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_q0_pos.cpp index ad4ab19fca3..11999393787 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_q0_pos.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_q0_pos.cpp @@ -5,25 +5,38 @@ // (exactly degenerate pairs are skipped); the nonlocal contraction // against an operator finite difference of . -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "source_base/constants.h" #include "source_base/matrix.h" #include "source_base/matrix3.h" #include "source_base/vector3.h" #include "source_cell/atom_pseudo.h" #include "source_cell/atom_spec.h" -#include "source_cell/qlist.h" -#include "source_cell/unitcell.h" #include "source_cell/stru_fac.h" #include "source_psi/psi.h" #include "source_pw/module_dfpt/dfpt_pert.h" #include "source_pw/module_dfpt/dfpt_q0.h" -// per-file fixture (self-contained, gtest style): this fixture is -// Gamma-only (k_d_ shadows the generic default k of the base) -class DFPTQ0SerialTest : public DFPTSerialBase +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities; Gamma-only +class DFPTQ0SerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; Structure_Factor sf_; ModuleDFPT::DFPT_Pert pert_; ModuleDFPT::DFPT_Q0 q0_; @@ -34,12 +47,19 @@ class DFPTQ0SerialTest : public DFPTSerialBase void SetUp() override { - SetUpCell(); - SetupBases(k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0), 4); + dfpt_test::setup_cell(ucell_, latvec_); + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, + k_d_, ModuleBase::Vector3(0.0, 0.0, 0.0), 4, q_cart_); pert_.init(ucell_, &pw_rho_, &pw_wfc_, sf_); q0_.init(ucell_, &pw_rho_, &pw_wfc_, &pert_); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + // wfc-basis index of the reciprocal vector (ix, iy, iz)/a at Gamma int IgOf(int ix, int iy, int iz) const { @@ -47,7 +67,9 @@ class DFPTQ0SerialTest : public DFPTSerialBase for (int ig = 0; ig < npwk; ++ig) { const ModuleBase::Vector3 g = pw_wfc_.getgpluskcar(0, ig); - if (std::llround(g.x * a_) == ix && std::llround(g.y * a_) == iy && std::llround(g.z * a_) == iz) + if (std::llround(g.x * dfpt_test::cubic_edge) == ix + && std::llround(g.y * dfpt_test::cubic_edge) == iy + && std::llround(g.z * dfpt_test::cubic_edge) == iz) { return ig; } @@ -56,8 +78,6 @@ class DFPTQ0SerialTest : public DFPTSerialBase } }; -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include #include #include @@ -120,7 +140,7 @@ TEST_F(DFPTQ0SerialTest, PosMatrixKineticAndDegenerateSkip) TEST_F(DFPTQ0SerialTest, PosMatrixNonlocalMatchesOperatorFiniteDifference) { - MakeNCAtom(); + dfpt_test::make_nc_atom(ucell_); const int npwk = pw_wfc_.npwk[0]; const int ig0 = IgOf(0, 0, 0); ASSERT_GE(ig0, 0); diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_rho.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_rho.cpp index 953f2f99246..df5407ed5ce 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_rho.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_rho.cpp @@ -6,15 +6,19 @@ #include "gtest/gtest.h" -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/constants.h" #include "source_base/matrix.h" -#include "source_basis/module_pw/pw_basis_k.h" #include "source_psi/psi.h" #include "source_pw/module_dfpt/dfpt_kq_basis.h" -#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_pw/module_dfpt/dfpt_rho.h" + namespace { @@ -35,18 +39,43 @@ std::complex crand() } // namespace -// per-file fixture (self-contained, gtest style) -class DFPTRhoSerialTest : public DFPTSerialBase +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTRhoSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; ModuleDFPT::DFPT_Rho rho_; static const int nbands_ = 2; + // default (k, q) of this file: q is generic and k = -q so k+q = 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + void SetUp() override { - DFPTSerialBase::SetUp(); - rho_.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, G_, std::string("rho_plain"), 0.4, 0.0}); + dfpt_test::setup_cell(ucell_, latvec_); + SetupRho(k_d_, q_d_); + } + + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + + // (re)initialize the bases and the plain rho mixer for a given (k, q) + void SetupRho(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d) + { + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d, q_d, nbands_, q_cart_); + rho_.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, ucell_.G, std::string("rho_plain"), 0.4, 0.0}); } void FillRandomStates(psi::Psi>& psi, @@ -114,7 +143,7 @@ TEST_F(DFPTRhoSerialTest, ComputeDrhoMatchesBruteForceGSpace) const int mx = (ix <= pw_rho_.nx / 2) ? ix : ix - pw_rho_.nx; const int my = (iy <= pw_rho_.ny / 2) ? iy : iy - pw_rho_.ny; const int mz = (iz <= pw_rho_.nz / 2) ? iz : iz - pw_rho_.nz; - const ModuleBase::Vector3 delta = ModuleBase::Vector3(mx, my, mz) * G_; + const ModuleBase::Vector3 delta = ModuleBase::Vector3(mx, my, mz) * ucell_.G; // A_Delta = (2 w / omega) * sum_G c*_G d_{G+Delta}: the spin factor // 2 sits in the band weight w1 = 2 w / omega (the QE incdrhoscf // convention, a915352cd), brute-forced over the lists @@ -212,8 +241,8 @@ TEST_F(DFPTRhoSerialTest, ChargeConservationAtGamma) // rebuild the fixture bases with q = k = 0 ModuleBase::Vector3 klist0[1] = {ModuleBase::Vector3(0.0, 0.0, 0.0)}; ModulePW::PW_Basis_K pw_wfc0; - pw_wfc0.initgrids(lat0_, latvec_, pw_rho_.nx, pw_rho_.ny, pw_rho_.nz); - pw_wfc0.initparameters(false, ecutwfc_, 1, klist0); + pw_wfc0.initgrids(dfpt_test::lat0, latvec_, pw_rho_.nx, pw_rho_.ny, pw_rho_.nz); + pw_wfc0.initparameters(false, dfpt_test::ecutwfc, 1, klist0); pw_wfc0.fft_bundle.initfftmode(0); pw_wfc0.setuptransform(); pw_wfc0.collect_local_pw(); @@ -225,7 +254,7 @@ TEST_F(DFPTRhoSerialTest, ChargeConservationAtGamma) ModuleDFPT::DFPT_PW_Data data0; data0.init(&qlist0, 1, nbands_, pw_wfc0.npwk_max, pw_rho_.nrxx, 1, 1, nullptr); ModuleDFPT::DFPT_Rho rho0; - rho0.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc0, G_, std::string("rho_plain"), 0.4, 0.0}); + rho0.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc0, ucell_.G, std::string("rho_plain"), 0.4, 0.0}); psi::Psi> psi(1, nbands_, pw_wfc0.npwk_max, pw_wfc0.npwk[0], true); ModuleDFPT::DFPT_KQ_Basis kq0; @@ -328,14 +357,14 @@ TEST_F(DFPTRhoSerialTest, VHartreeQClosedFormAndZeroMode) // --------------------------------------------------------------------------- TEST_F(DFPTRhoSerialTest, DrhocLocalizesAtTheAtomNotItsMirror) { - MakeNlccTwoAtomCell(); - SetupBases(k_d_, q_d_, 2); + dfpt_test::make_nlcc_two_atom_cell(ucell_); + SetupRho(k_d_, q_d_); ModuleDFPT::DFPT_Rho::Config cfg; cfg.nspin = 1; cfg.nrxx = pw_rho_.nrxx; cfg.pw_rho = &pw_rho_; cfg.pw_wfc = &pw_wfc_; - cfg.recip_matrix = G_; + cfg.recip_matrix = ucell_.G; cfg.mix_type = "rho_plain"; cfg.mix_beta = 0.4; cfg.kerker_a2 = 1.0; @@ -350,7 +379,7 @@ TEST_F(DFPTRhoSerialTest, DrhocLocalizesAtTheAtomNotItsMirror) ASSERT_TRUE(ucell_.atoms[it].ncpp.nlcc); // tau is stored in lat0-cartesian units (fixture convention: cell edge // a_ in lat0 units); the true fractional position divides by a_ - const double inv_a = 1.0 / a_; + const double inv_a = 1.0 / dfpt_test::cubic_edge; const ModuleBase::Vector3 tau_frac(ucell_.atoms[it].tau[ucell_.iat2ia[iat]].x * inv_a, ucell_.atoms[it].tau[ucell_.iat2ia[iat]].y * inv_a, ucell_.atoms[it].tau[ucell_.iat2ia[iat]].z * inv_a); diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_rho_mix.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_rho_mix.cpp index 05e40a52bcb..60dbb7a2f8f 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_rho_mix.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_rho_mix.cpp @@ -6,15 +6,19 @@ #include "gtest/gtest.h" -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_base/constants.h" #include "source_base/matrix.h" -#include "source_basis/module_pw/pw_basis_k.h" #include "source_psi/psi.h" #include "source_pw/module_dfpt/dfpt_kq_basis.h" -#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_pw/module_dfpt/dfpt_rho.h" + namespace { @@ -35,18 +39,43 @@ std::complex crand() } // namespace -// per-file fixture (self-contained, gtest style) -class DFPTRhoSerialTest : public DFPTSerialBase +// per-file self-contained fixture (gtest style): owns its objects and +// calls the dfpt_test free-function utilities +class DFPTRhoSerialTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModulePW::PW_Basis pw_rho_; + ModulePW::PW_Basis_K pw_wfc_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + ModuleBase::Vector3 q_cart_; ModuleDFPT::DFPT_Rho rho_; static const int nbands_ = 2; + // default (k, q) of this file: q is generic and k = -q so k+q = 0 + const ModuleBase::Vector3 k_d_{-0.13, 0.0, -0.07}; + const ModuleBase::Vector3 q_d_{0.13, 0.0, 0.07}; + void SetUp() override { - DFPTSerialBase::SetUp(); - rho_.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, G_, std::string("rho_plain"), 0.4, 0.0}); + dfpt_test::setup_cell(ucell_, latvec_); + SetupRho(k_d_, q_d_); + } + + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + + // (re)initialize the bases and the plain rho mixer for a given (k, q) + void SetupRho(const ModuleBase::Vector3& k_d, const ModuleBase::Vector3& q_d) + { + dfpt_test::setup_bases(pw_rho_, pw_wfc_, qlist_, data_, ucell_, k_d, q_d, nbands_, q_cart_); + rho_.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, ucell_.G, std::string("rho_plain"), 0.4, 0.0}); } void FillRandomStates(psi::Psi>& psi, @@ -150,7 +179,7 @@ TEST_F(DFPTRhoSerialTest, MixDrhoKerkerFirstStepIsPreconditionedScaledOutput) } ASSERT_GT(w2_min, 0.0); const double a2 = 4.0 * w2_min; - rho_k.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, G_, std::string("rho_kerker"), 0.7, a2}); + rho_k.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, ucell_.G, std::string("rho_kerker"), 0.7, a2}); std::vector> out(pw_rho_.npw); int n_small = 0; @@ -240,7 +269,7 @@ TEST_F(DFPTRhoSerialTest, MixDrhoKerkerStabilizesStiffModelProblem) // plain beta = 0.7 on the stiff model diverges ModuleDFPT::DFPT_Rho rho_p; - rho_p.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, G_, std::string("rho_plain"), 0.7, 0.0}); + rho_p.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, ucell_.G, std::string("rho_plain"), 0.7, 0.0}); data_.set_drho_g(0, 0, std::vector>(npw, std::complex(0.0, 0.0))); for (int it = 0; it < 40; ++it) { @@ -253,7 +282,7 @@ TEST_F(DFPTRhoSerialTest, MixDrhoKerkerStabilizesStiffModelProblem) // kerker beta = 0.7 with a^2 = 9 w2_min (f ~ 0.1 on the stiff shell) // converges to the target ModuleDFPT::DFPT_Rho rho_k; - rho_k.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, G_, std::string("rho_kerker"), 0.7, 9.0 * w2_min}); + rho_k.init({1, pw_rho_.nrxx, &pw_rho_, &pw_wfc_, ucell_.G, std::string("rho_kerker"), 0.7, 9.0 * w2_min}); data_.set_drho_g(0, 0, std::vector>(npw, std::complex(0.0, 0.0))); for (int it = 0; it < 300; ++it) { @@ -280,13 +309,13 @@ TEST_F(DFPTRhoSerialTest, BroydenConvergesDiagonalSystemFast) // the binding shell; 0.72 for d = 0.3), i.e. roughly 100 plain // iterations to 1e-10; Modified Broyden on the few distinct // eigenvalues should converge in a handful of iterations. - SetupBases(k_d_, q_d_, 2); + SetupRho(k_d_, q_d_); ModuleDFPT::DFPT_Rho::Config cfg; cfg.nspin = 1; cfg.nrxx = pw_rho_.nrxx; cfg.pw_rho = &pw_rho_; cfg.pw_wfc = &pw_wfc_; - cfg.recip_matrix = G_; + cfg.recip_matrix = ucell_.G; cfg.mix_type = "rho_broyden"; cfg.mix_beta = 0.4; cfg.kerker_a2 = 1.0; @@ -349,13 +378,13 @@ TEST_F(DFPTRhoSerialTest, BroydenResetMixingAllowsFreshQAndSameQCycle) // allocated Mixing_Data exits through the one-object-one-data guard // (broyden_mixing.cpp) and kills the process. The same-q cycle must // also restart from a zero input density. - SetupBases(k_d_, q_d_, 2); + SetupRho(k_d_, q_d_); ModuleDFPT::DFPT_Rho::Config cfg; cfg.nspin = 1; cfg.nrxx = pw_rho_.nrxx; cfg.pw_rho = &pw_rho_; cfg.pw_wfc = &pw_wfc_; - cfg.recip_matrix = G_; + cfg.recip_matrix = ucell_.G; cfg.mix_type = "rho_broyden"; cfg.mix_beta = 0.4; cfg.kerker_a2 = 1.0; diff --git a/source/source_pw/module_dfpt/test_serial/test_dfpt_write_dyn.cpp b/source/source_pw/module_dfpt/test_serial/test_dfpt_write_dyn.cpp index b77e56b5ee7..bfe4f6173d6 100644 --- a/source/source_pw/module_dfpt/test_serial/test_dfpt_write_dyn.cpp +++ b/source/source_pw/module_dfpt/test_serial/test_dfpt_write_dyn.cpp @@ -1,6 +1,14 @@ -#include "dfpt_serial_fixture.h" +#include "dfpt_test_tool.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_basis/module_pw/pw_basis_k.h" +#include "source_cell/qlist.h" +#include "source_cell/unitcell.h" +#include "source_base/complexmatrix.h" +#include "source_pw/module_dfpt/dfpt_pw_data.h" #include "source_pw/module_dfpt/dfpt_write_dyn.h" +#include "gtest/gtest.h" + #include "source_base/matrix.h" #include "source_base/matrix3.h" #include "source_base/vector3.h" @@ -20,17 +28,32 @@ /// Writer-focused fixture: only the two-atom cell geometry and the /// qlist/data binding are needed; the plane-wave bases of the base /// SetUp are not on the writer path. -class DFPTWriteDynTest : public DFPTSerialBase +// Writer-focused fixture: only the two-atom cell geometry and the +// qlist/data binding are needed; the plane-wave bases are not on the +// writer path. Self-contained gtest style, calling the dfpt_test +// free-function utilities. +class DFPTWriteDynTest : public ::testing::Test { protected: + ModuleBase::Matrix3 latvec_; + UnitCell ucell_; + ModuleCell::QList qlist_; + ModuleDFPT::DFPT_PW_Data data_; + void SetUp() override { - SetUpCell(); - MakeTwoAtomCell(); - // bind data to qlist exactly like SetupBases does (nat = 2) + dfpt_test::setup_cell(ucell_, latvec_); + dfpt_test::make_two_atom_cell(ucell_); + // bind data to qlist exactly like setup_bases does (nat = 2) data_.init(&qlist_, 1, 1, 1, 1, 1, 2, nullptr); } + void TearDown() override + { + delete[] ucell_.atoms; + ucell_.atoms = nullptr; + } + /// install an explicit q list (Gamma-first or not) into qlist_ void set_qvecs(const std::vector>& qvecs) {