From 98e3f81cf425070fe58e8dd191dfbfbb7be51e6d Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 07:22:02 +0800 Subject: [PATCH 01/21] Fix #5021 #5939: warn on ignored x/y magnetization and remove nspin=4 autoset When nspin=4 with noncolin=0, the x/y components of the initial magnetization from STRU are silently zeroed. Now a warning is printed to both stdout and running_scf.log, telling the user that x/y are ignored and suggesting to set noncolin=1. When nspin=4 and no initial magnetization is set in STRU, the code previously auto-assigned (1,1,1) to all atoms. This is removed; the calculation now starts from zero magnetic moment with a warning, letting the user decide explicitly. nspin=2 autoset behavior is unchanged. Also adds unit tests for both behaviors and updates parameter docs. --- docs/advanced/input_files/input-main.md | 2 + docs/parameters.yaml | 2 + source/source_cell/read_atoms_helper.cpp | 33 +-- .../source_cell/test/read_atoms_hlp_test.cpp | 204 ++++++++++++++++++ 4 files changed, 228 insertions(+), 13 deletions(-) diff --git a/docs/advanced/input_files/input-main.md b/docs/advanced/input_files/input-main.md index 4e5456899d3..bacad14219c 100644 --- a/docs/advanced/input_files/input-main.md +++ b/docs/advanced/input_files/input-main.md @@ -1604,6 +1604,8 @@ - noncolin=0, lspinorb=1: SOC with z-axis magnetism only (for non-magnetic materials with SOC) - noncolin=1, lspinorb=0: Non-collinear magnetism without SOC - noncolin=1, lspinorb=1: Both non-collinear magnetism and SOC + - Note: When nspin=4 and noncolin=0, only the z component of the initial magnetization in STRU is used; x/y components are ignored and a warning is printed. + - Note: When nspin=4 and no initial magnetization is set in STRU, the calculation starts from zero magnetic moment; no automatic magnetization is assigned. - **Default**: False ### soc_lambda diff --git a/docs/parameters.yaml b/docs/parameters.yaml index 813d25e3b0a..02b5e0d5e91 100644 --- a/docs/parameters.yaml +++ b/docs/parameters.yaml @@ -996,6 +996,8 @@ parameters: * noncolin=0, lspinorb=1: SOC with z-axis magnetism only (for non-magnetic materials with SOC) * noncolin=1, lspinorb=0: Non-collinear magnetism without SOC * noncolin=1, lspinorb=1: Both non-collinear magnetism and SOC + * Note: When nspin=4 and noncolin=0, only the z component of the initial magnetization in STRU is used; x/y components are ignored and a warning is printed. + * Note: When nspin=4 and no initial magnetization is set in STRU, the calculation starts from zero magnetic moment; no automatic magnetization is assigned. default_value: "False" unit: "" availability: "" diff --git a/source/source_cell/read_atoms_helper.cpp b/source/source_cell/read_atoms_helper.cpp index 8e5e75e6c9a..c440d7ef8af 100644 --- a/source/source_cell/read_atoms_helper.cpp +++ b/source/source_cell/read_atoms_helper.cpp @@ -102,19 +102,14 @@ void autoset_magnetization(UnitCell& ucell, int nspin, { if(nspin==4) { - for (int it = 0; it < ntype; it++) - { - for (int ia = 0; ia < ucell.atoms[it].na; ia++) - { - ucell.atoms[it].m_loc_[ia].x = 1.0; - ucell.atoms[it].m_loc_[ia].y = 1.0; - ucell.atoms[it].m_loc_[ia].z = 1.0; - ucell.atoms[it].mag[ia] = sqrt(pow(ucell.atoms[it].m_loc_[ia].x,2) - +pow(ucell.atoms[it].m_loc_[ia].y,2) - +pow(ucell.atoms[it].m_loc_[ia].z,2)); - ModuleBase::GlobalFunc::OUT(ofs_running,"Autoset magnetism for this atom", 1.0, 1.0, 1.0); - } - } + // nspin=4 with all-zero magnetization: do NOT autoset, + // start from zero and let the user decide explicitly. + std::string msg = "nspin=4 but no initial magnetization is set in STRU.\n" + " All atoms start from zero magnetic moment.\n" + " If a magnetic ground state is expected, set 'mag' explicitly " + "in STRU for the magnetic atoms."; + std::cout << " Warning: " << msg << std::endl; + ModuleBase::GlobalFunc::OUT(ofs_running, "Warning", msg); } else if(nspin==2) { @@ -307,6 +302,18 @@ void process_magnetization(Atom& atom, int it, int ia, if(!noncolin) { // collinear case with nspin = 4, only z component is used + if(std::abs(atom.m_loc_[ia].x) > 1e-5 || std::abs(atom.m_loc_[ia].y) > 1e-5) + { + std::stringstream ss; + ss << "Atom " << ia+1 << " of type " << atom.label + << ": STRU gives non-zero x/y magnetization (" + << atom.m_loc_[ia].x << ", " << atom.m_loc_[ia].y << ", " + << atom.m_loc_[ia].z << "), but nspin=4 with noncolin=0 " + << "is a collinear calculation; only the z component is used.\n" + << " x/y components are IGNORED. Set 'noncolin 1' to use the full vector."; + std::cout << " Warning: " << ss.str() << std::endl; + ModuleBase::GlobalFunc::OUT(ofs_running, "Warning", ss.str()); + } atom.m_loc_[ia].x = 0; atom.m_loc_[ia].y = 0; } diff --git a/source/source_cell/test/read_atoms_hlp_test.cpp b/source/source_cell/test/read_atoms_hlp_test.cpp index 77e6c49dc11..9d0e8b15b11 100644 --- a/source/source_cell/test/read_atoms_hlp_test.cpp +++ b/source/source_cell/test/read_atoms_hlp_test.cpp @@ -478,3 +478,207 @@ int main(int argc, char **argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +// ============================================================ +// Tests for issue #5021: nspin=4 with noncolin=0 should warn +// when x/y components are non-zero +// ============================================================ + +TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearWarnsOnXY) +{ + Atom atom; + atom.label = "Fe"; + atom.mag.resize(1); + atom.m_loc_.resize(1); + atom.angle1.resize(1); + atom.angle2.resize(1); + + atom.m_loc_[0].set(1.0, 0.5, 0.3); + atom.mag[0] = sqrt(1.0 + 0.25 + 0.09); + + const int nspin = 4; + const bool input_vec_mag = true; + const bool input_angle_mag = false; + const bool noncolin = false; + + // Capture stdout + testing::internal::CaptureStdout(); + unitcell::process_magnetization(atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); + std::string stdout_output = testing::internal::GetCapturedStdout(); + + // x/y should be zeroed + EXPECT_DOUBLE_EQ(atom.m_loc_[0].x, 0.0); + EXPECT_DOUBLE_EQ(atom.m_loc_[0].y, 0.0); + EXPECT_DOUBLE_EQ(atom.m_loc_[0].z, 0.3); + + // Warning should appear on stdout + EXPECT_NE(stdout_output.find("Warning"), std::string::npos); + EXPECT_NE(stdout_output.find("x/y components are IGNORED"), std::string::npos); + + // Warning should appear in running log + ofs_running.flush(); + std::ifstream ifs("test_running.log"); + std::stringstream buffer; + buffer << ifs.rdbuf(); + std::string log_content = buffer.str(); + ifs.close(); + EXPECT_NE(log_content.find("Warning"), std::string::npos); + EXPECT_NE(log_content.find("x/y components are IGNORED"), std::string::npos); +} + +TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearNoWarnOnZeroXY) +{ + Atom atom; + atom.label = "Fe"; + atom.mag.resize(1); + atom.m_loc_.resize(1); + atom.angle1.resize(1); + atom.angle2.resize(1); + + atom.m_loc_[0].set(0.0, 0.0, 0.3); + atom.mag[0] = 0.3; + + const int nspin = 4; + const bool input_vec_mag = true; + const bool input_angle_mag = false; + const bool noncolin = false; + + testing::internal::CaptureStdout(); + unitcell::process_magnetization(atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); + std::string stdout_output = testing::internal::GetCapturedStdout(); + + EXPECT_DOUBLE_EQ(atom.m_loc_[0].x, 0.0); + EXPECT_DOUBLE_EQ(atom.m_loc_[0].y, 0.0); + EXPECT_DOUBLE_EQ(atom.m_loc_[0].z, 0.3); + + // No warning should appear + EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); +} + +TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4NoncolinNoWarn) +{ + Atom atom; + atom.label = "Fe"; + atom.mag.resize(1); + atom.m_loc_.resize(1); + atom.angle1.resize(1); + atom.angle2.resize(1); + + atom.m_loc_[0].set(1.0, 0.5, 0.3); + atom.mag[0] = sqrt(1.0 + 0.25 + 0.09); + + const int nspin = 4; + const bool input_vec_mag = true; + const bool input_angle_mag = false; + const bool noncolin = true; + + testing::internal::CaptureStdout(); + unitcell::process_magnetization(atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); + std::string stdout_output = testing::internal::GetCapturedStdout(); + + // All components should be preserved + EXPECT_DOUBLE_EQ(atom.m_loc_[0].x, 1.0); + EXPECT_DOUBLE_EQ(atom.m_loc_[0].y, 0.5); + EXPECT_DOUBLE_EQ(atom.m_loc_[0].z, 0.3); + + // No warning should appear + EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); +} + +// ============================================================ +// Tests for issue #5939: nspin=4 autoset should not force (1,1,1) +// ============================================================ + +TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin4NoAutoset) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 1; + ucell.atoms[0].mag.resize(1, 0.0); + ucell.atoms[0].m_loc_.resize(1); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 0.0); + + const int nspin = 4; + + testing::internal::CaptureStdout(); + unitcell::autoset_magnetization(ucell, nspin, ofs_running); + std::string stdout_output = testing::internal::GetCapturedStdout(); + + // Magnetization should remain zero (NOT autoset to (1,1,1)) + EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].x, 0.0); + EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].y, 0.0); + EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 0.0); + EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 0.0); + + // Warning should appear on stdout + EXPECT_NE(stdout_output.find("Warning"), std::string::npos); + EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), std::string::npos); + + // Warning should appear in running log + ofs_running.flush(); + std::ifstream ifs("test_running.log"); + std::stringstream buffer; + buffer << ifs.rdbuf(); + std::string log_content = buffer.str(); + ifs.close(); + EXPECT_NE(log_content.find("Warning"), std::string::npos); + EXPECT_NE(log_content.find("no initial magnetization is set in STRU"), std::string::npos); + + delete[] ucell.atoms; +} + +TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin4NoWarnWhenMagSet) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 1; + ucell.atoms[0].mag.resize(1, 1.5); + ucell.atoms[0].m_loc_.resize(1); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 1.5); + + const int nspin = 4; + + testing::internal::CaptureStdout(); + unitcell::autoset_magnetization(ucell, nspin, ofs_running); + std::string stdout_output = testing::internal::GetCapturedStdout(); + + // No autoset should happen + EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 1.5); + EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 1.5); + + // No warning should appear + EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); + + delete[] ucell.atoms; +} + +TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin2StillAutoset) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 1; + ucell.atoms[0].mag.resize(1, 0.0); + ucell.atoms[0].m_loc_.resize(1); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 0.0); + + const int nspin = 2; + + testing::internal::CaptureStdout(); + unitcell::autoset_magnetization(ucell, nspin, ofs_running); + std::string stdout_output = testing::internal::GetCapturedStdout(); + + // nspin=2 should still autoset to 1.0 (VASP-compatible behavior) + EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 1.0); + EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 1.0); + + // No warning for nspin=2 autoset + EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); + + delete[] ucell.atoms; +} From 9d8317cb1f7334752d085cc57454a3b2e2173c3e Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 09:57:00 +0800 Subject: [PATCH 02/21] Fix parameters.yaml sync: add noncolin notes to C++ description source docs/parameters.yaml is generated from item.description in C++ source. Commit 98e3f81cf added two noncolin notes to the generated yaml and input-main.md but omitted the generator source, making the yaml consistency CI check fail. Add the same two notes to the noncolin description in read_inp_estruc.cpp so regeneration matches. --- source/source_io/module_parameter/read_inp_estruc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/source_io/module_parameter/read_inp_estruc.cpp b/source/source_io/module_parameter/read_inp_estruc.cpp index 49c5fec46b3..11247ebea7f 100644 --- a/source/source_io/module_parameter/read_inp_estruc.cpp +++ b/source/source_io/module_parameter/read_inp_estruc.cpp @@ -1006,7 +1006,9 @@ Note: If gamma_only is set to 1, the KPT file will be overwritten. So make sure * Relationship with lspinorb: * noncolin=0, lspinorb=1: SOC with z-axis magnetism only (for non-magnetic materials with SOC) * noncolin=1, lspinorb=0: Non-collinear magnetism without SOC - * noncolin=1, lspinorb=1: Both non-collinear magnetism and SOC)"; + * noncolin=1, lspinorb=1: Both non-collinear magnetism and SOC +* Note: When nspin=4 and noncolin=0, only the z component of the initial magnetization in STRU is used; x/y components are ignored and a warning is printed. +* Note: When nspin=4 and no initial magnetization is set in STRU, the calculation starts from zero magnetic moment; no automatic magnetization is assigned.)"; item.default_value = "False"; item.unit = ""; read_sync_bool(input.noncolin); From ffaeba6ec16d5dbcf3ce1cd5d866264fbf728fba Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 11:52:14 +0800 Subject: [PATCH 03/21] Test: update ReadAtomPositionsAutosetMag for nspin=4 zero-moment start Follow-up to #5939: nspin=4 with no mag in STRU no longer autosets (1,1,1); the unit test still expected the old forced moments. Assert zero magnetization instead and verify the new warning on both stdout and the running log. --- source/source_cell/test/unitcell_test.cpp | 26 +++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index 22ed1fd421c..a633263a21f 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -1582,21 +1582,39 @@ TEST_F(UcellTestReadStru, ReadAtomPositionsAutosetMag) } } // for nspin == 4 + // Issue #5939: nspin=4 with no mag in STRU no longer autosets (1,1,1); + // all moments stay zero and a warning is emitted instead. nspin = 4; + testing::internal::CaptureStdout(); unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, basis_type, orbital_dir, init_wfc, onsite_radius, fixed_atoms, noncolin, calculation, esolver_type, 0); + const std::string stdout_output = testing::internal::GetCapturedStdout(); for (int it = 0; it < ucell->ntype; it++) { for (int ia = 0; ia < ucell->atoms[it].na; ia++) { - EXPECT_DOUBLE_EQ(ucell->atoms[it].mag[ia], sqrt(pow(1.0, 2) + pow(1.0, 2) + pow(1.0, 2))); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].x, 1.0); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].y, 1.0); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].z, 1.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].mag[ia], 0.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].x, 0.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].y, 0.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].z, 0.0); } } + // The zero-moment warning must reach both stdout and the running log. + EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), + std::string::npos); + ofs_running.flush(); + std::ifstream ifs_log("read_atom_positions.tmp"); + std::string log_content; + std::string log_line; + while (std::getline(ifs_log, log_line)) + { + log_content += log_line; + } + ifs_log.close(); + EXPECT_NE(log_content.find("no initial magnetization is set in STRU"), + std::string::npos); ofs_running.close(); ofs_warning.close(); ifa.close(); From 5306acf61e2d8614b7cb63723bc8ea3e8c17bd5d Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 12:06:37 +0800 Subject: [PATCH 04/21] Test: split cal_ux tests out of MODULE_CELL_unitcell_test Move JudgeParallel, CalUx1 and CalUx2 to the new test_cal_ux.cpp / MODULE_CELL_cal_ux_test target, one test file per source file. The new target compiles ../cal_ux.cpp directly like the old target did; unitcell_test no longer needs it. --- source/source_cell/test/CMakeLists.txt | 8 ++- source/source_cell/test/test_cal_ux.cpp | 87 +++++++++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 49 ------------- 3 files changed, 94 insertions(+), 50 deletions(-) create mode 100644 source/source_cell/test/test_cal_ux.cpp diff --git a/source/source_cell/test/CMakeLists.txt b/source/source_cell/test/CMakeLists.txt index e0db610c5bf..44fd1466832 100644 --- a/source/source_cell/test/CMakeLists.txt +++ b/source/source_cell/test/CMakeLists.txt @@ -173,7 +173,13 @@ add_test(NAME MODULE_CELL_parallel_kpoints_test AddTest( TARGET MODULE_CELL_unitcell_test LIBS base device cell_info symmetry - SOURCES unitcell_test.cpp ../cal_ux.cpp + SOURCES unitcell_test.cpp +) + +AddTest( + TARGET MODULE_CELL_cal_ux_test + LIBS base device cell_info + SOURCES test_cal_ux.cpp ../cal_ux.cpp ) AddTest( diff --git a/source/source_cell/test/test_cal_ux.cpp b/source/source_cell/test/test_cal_ux.cpp new file mode 100644 index 00000000000..4ee3c864456 --- /dev/null +++ b/source/source_cell/test/test_cal_ux.cpp @@ -0,0 +1,87 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include + +#include "source_base/global_variable.h" +#include "source_base/mathzone.h" +#include "source_cell/unitcell.h" +#include "source_cell/cal_ux.h" +#include "prepare_unitcell.h" + +// The test-only cell_info object library does not contain magnetism.cpp, +// so the Magnetism constructor/destructor must be provided locally. +Magnetism::Magnetism() +{ + this->tot_mag = 0.0; + this->abs_mag = 0.0; +} +Magnetism::~Magnetism() +{ +} + +/************************************************ + * unit test of cal_ux.cpp + ***********************************************/ + +/** + * - Tested Functions: + * - JudgeParallel + * - judge_parallel: judge if two vectors a[3] and Vector3 b are parallel + * - CalUx1 + * - cal_ux: non-parallel initial moments, no common direction + * - CalUx2 + * - cal_ux: parallel moments, normalized common direction (1,1,1)/sqrt(3) + */ + +class CalUxTest : public ::testing::Test +{ + protected: + std::unique_ptr ucell; +}; + +TEST_F(CalUxTest, JudgeParallel) +{ + ModuleBase::Vector3 b(1.0, 1.0, 1.0); + double a[3] = {1.0, 1.0, 1.0}; + EXPECT_TRUE(unitcell::judge_parallel(a, b)); + + // the negative case, moved here from MagnetismTest.JudgeParallel when the + // duplicate Magnetism::judge_parallel was deleted + double c[3] = {1.0, 0.0, 0.0}; + ModuleBase::Vector3 d(0.0, 1.0, 0.0); + EXPECT_FALSE(unitcell::judge_parallel(c, d)); +} + +TEST_F(CalUxTest, CalUx1) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; + ucell = utp.SetUcellInfo(); + ucell->atoms[0].m_loc_[0].set(0, -1, 0); + ucell->atoms[1].m_loc_[0].set(1, 1, 1); + ucell->atoms[1].m_loc_[1].set(0, 0, 0); + const int nspin = 4; + unitcell::cal_ux(*ucell, nspin); + EXPECT_FALSE(ucell->magnet.lsign_); + EXPECT_DOUBLE_EQ(ucell->magnet.ux_[0], 0); + EXPECT_DOUBLE_EQ(ucell->magnet.ux_[1], -1); + EXPECT_DOUBLE_EQ(ucell->magnet.ux_[2], 0); +} + +TEST_F(CalUxTest, CalUx2) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; + ucell = utp.SetUcellInfo(); + ucell->atoms[0].m_loc_[0].set(0, 0, 0); + ucell->atoms[1].m_loc_[0].set(1, 1, 1); + ucell->atoms[1].m_loc_[1].set(0, 0, 0); + //(0,0,0) is also parallel to (1,1,1) + const int nspin = 4; + unitcell::cal_ux(*ucell, nspin); + EXPECT_TRUE(ucell->magnet.lsign_); + EXPECT_NEAR(ucell->magnet.ux_[0], 0.57735, 1e-5); + EXPECT_NEAR(ucell->magnet.ux_[1], 0.57735, 1e-5); + EXPECT_NEAR(ucell->magnet.ux_[2], 0.57735, 1e-5); +} diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index a633263a21f..278be9dd0af 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -1,7 +1,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "source_cell/cal_ux.h" #include "source_cell/read_orb.h" #include "source_cell/read_pp_ucell.h" #include "source_cell/read_stru.h" @@ -43,8 +42,6 @@ Magnetism::~Magnetism() * - remake_cell(): rebuild cell according to its latName * - RemakeCellWarnings * - remake_cell(): deliver warnings when find wrong latname or cos12 - * - JudgeParallel - * - judge_parallel: judge if two vectors a[3] and Vector3 b are parallel * - Index * - set_iat2iait(): set index relations in two arrays of Unitcell: iat2it[nat], iat2ia[nat] * - iat2iait(): depends on the above function, but can find both ia & it from iat @@ -73,8 +70,6 @@ Magnetism::~Magnetism() * - Actually an integrated function to call unitcell::print_cell and Atom::print_Atom * - UpdateVel * - update_vel(const ModuleBase::Vector3* vel_in) - * - CalUx - * - cal_ux(UnitCell& ucell): calculate magnetic moments of cell * - ReadOrbFile * - read_orb_file(): read header part of orbital file * - ReadOrbFileWarning @@ -539,19 +534,6 @@ TEST_F(UcellDeathTest, RemakeCellWarnings) } } -TEST_F(UcellTest, JudgeParallel) -{ - ModuleBase::Vector3 b(1.0, 1.0, 1.0); - double a[3] = {1.0, 1.0, 1.0}; - EXPECT_TRUE(unitcell::judge_parallel(a, b)); - - // the negative case, moved here from MagnetismTest.JudgeParallel when the - // duplicate Magnetism::judge_parallel was deleted - double c[3] = {1.0, 0.0, 0.0}; - ModuleBase::Vector3 d(0.0, 1.0, 0.0); - EXPECT_FALSE(unitcell::judge_parallel(c, d)); -} - TEST_F(UcellTest, Index) { UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; @@ -855,37 +837,6 @@ TEST_F(UcellTest, UpdateVel) delete[] vel_in; } -TEST_F(UcellTest, CalUx1) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; - ucell = utp.SetUcellInfo(); - ucell->atoms[0].m_loc_[0].set(0, -1, 0); - ucell->atoms[1].m_loc_[0].set(1, 1, 1); - ucell->atoms[1].m_loc_[1].set(0, 0, 0); - const int nspin = 4; - unitcell::cal_ux(*ucell, nspin); - EXPECT_FALSE(ucell->magnet.lsign_); - EXPECT_DOUBLE_EQ(ucell->magnet.ux_[0], 0); - EXPECT_DOUBLE_EQ(ucell->magnet.ux_[1], -1); - EXPECT_DOUBLE_EQ(ucell->magnet.ux_[2], 0); -} - -TEST_F(UcellTest, CalUx2) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; - ucell = utp.SetUcellInfo(); - ucell->atoms[0].m_loc_[0].set(0, 0, 0); - ucell->atoms[1].m_loc_[0].set(1, 1, 1); - ucell->atoms[1].m_loc_[1].set(0, 0, 0); - //(0,0,0) is also parallel to (1,1,1) - const int nspin = 4; - unitcell::cal_ux(*ucell, nspin); - EXPECT_TRUE(ucell->magnet.lsign_); - EXPECT_NEAR(ucell->magnet.ux_[0], 0.57735, 1e-5); - EXPECT_NEAR(ucell->magnet.ux_[1], 0.57735, 1e-5); - EXPECT_NEAR(ucell->magnet.ux_[2], 0.57735, 1e-5); -} - #ifdef __LCAO TEST_F(UcellTest, ReadOrbFile) { From 18fb06b38791628d27f5425663cf1b0cd328fc39 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 12:23:05 +0800 Subject: [PATCH 05/21] Test: split read_orb tests out of MODULE_CELL_unitcell_test Move ReadOrbFile (kept under the __LCAO guard as before) and ReadOrbFileWarning to the new test_read_orb.cpp / MODULE_CELL_read_orb_test target. read_orb.cpp is already part of the test-only cell_info object library, so no extra source is compiled in. --- source/source_cell/test/CMakeLists.txt | 6 ++ source/source_cell/test/test_read_orb.cpp | 79 +++++++++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 37 ----------- 3 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 source/source_cell/test/test_read_orb.cpp diff --git a/source/source_cell/test/CMakeLists.txt b/source/source_cell/test/CMakeLists.txt index 44fd1466832..fd80397a14a 100644 --- a/source/source_cell/test/CMakeLists.txt +++ b/source/source_cell/test/CMakeLists.txt @@ -182,6 +182,12 @@ AddTest( SOURCES test_cal_ux.cpp ../cal_ux.cpp ) +AddTest( + TARGET MODULE_CELL_read_orb_test + LIBS base device cell_info + SOURCES test_read_orb.cpp +) + AddTest( TARGET MODULE_CELL_print_cell_test LIBS base device cell_info symmetry diff --git a/source/source_cell/test/test_read_orb.cpp b/source/source_cell/test/test_read_orb.cpp new file mode 100644 index 00000000000..1d8281268b0 --- /dev/null +++ b/source/source_cell/test/test_read_orb.cpp @@ -0,0 +1,79 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include + +#include "source_base/mathzone.h" +#include "source_cell/unitcell.h" +#include "source_cell/read_orb.h" +#include "prepare_unitcell.h" + +// The test-only cell_info object library does not contain magnetism.cpp, +// so the Magnetism constructor/destructor must be provided locally. +Magnetism::Magnetism() +{ + this->tot_mag = 0.0; + this->abs_mag = 0.0; +} +Magnetism::~Magnetism() +{ +} + +/************************************************ + * unit test of read_orb.cpp + ***********************************************/ + +/** + * - Tested Functions: + * - ReadOrbFile + * - read_orb_file(): read header part of orbital file + * - ReadOrbFileWarning + * - read_orb_file(): ABACUS cannot find the ORBITAL file + */ + +class ReadOrbTest : public ::testing::Test +{ + protected: + std::unique_ptr ucell; + std::string output; +}; + +#ifdef __LCAO +TEST_F(ReadOrbTest, ReadOrbFile) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; + ucell = utp.SetUcellInfo(); + std::string orb_file = "./support/C.orb"; + std::ofstream ofs_running; + ofs_running.open("tmp_readorbfile"); + bool result = unitcell::read_orb_file(0, orb_file, ofs_running, &(ucell->atoms[0])); + ofs_running << " result=" << result << std::endl; + EXPECT_TRUE(result); + ofs_running.close(); + EXPECT_EQ(ucell->atoms[0].nw, 25); + remove("tmp_readorbfile"); +} +#endif + +TEST_F(ReadOrbTest, ReadOrbFileWarning) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; + ucell = utp.SetUcellInfo(); + std::string orb_file = "./support/CC.orb"; + std::ofstream ofs_running; + ofs_running.open("tmp_readorbfilewarning"); + testing::internal::CaptureStdout(); + bool result = unitcell::read_orb_file(0, orb_file, ofs_running, &(ucell->atoms[0])); + output = testing::internal::GetCapturedStdout(); + ofs_running << output << std::endl; + EXPECT_FALSE(result); + EXPECT_THAT(output, testing::HasSubstr("Element index 1")); + EXPECT_THAT(output, testing::HasSubstr("orbital file: ./support/CC.orb")); + ofs_running.close(); + remove("tmp_readorbfilewarning"); +} diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index 278be9dd0af..588d62b2dff 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -1,7 +1,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "source_cell/read_orb.h" #include "source_cell/read_pp_ucell.h" #include "source_cell/read_stru.h" #include "source_cell/cell_tools.h" @@ -70,10 +69,6 @@ Magnetism::~Magnetism() * - Actually an integrated function to call unitcell::print_cell and Atom::print_Atom * - UpdateVel * - update_vel(const ModuleBase::Vector3* vel_in) - * - ReadOrbFile - * - read_orb_file(): read header part of orbital file - * - ReadOrbFileWarning - * - read_orb_file(): ABACUS Cannot find the ORBITAL file * - ReadAtomSpecies * - read_atom_species(): a successful case * - ReadAtomSpeciesWarning1 @@ -838,21 +833,6 @@ TEST_F(UcellTest, UpdateVel) } #ifdef __LCAO -TEST_F(UcellTest, ReadOrbFile) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; - ucell = utp.SetUcellInfo(); - std::string orb_file = "./support/C.orb"; - std::ofstream ofs_running; - ofs_running.open("tmp_readorbfile"); - bool result = unitcell::read_orb_file(0, orb_file, ofs_running, &(ucell->atoms[0])); - ofs_running << " result=" << result << std::endl; - EXPECT_TRUE(result); - ofs_running.close(); - EXPECT_EQ(ucell->atoms[0].nw, 25); - remove("tmp_readorbfile"); -} - class UcellTestReadStru : public ::testing::Test { protected: @@ -1812,20 +1792,3 @@ TEST_F(UcellTestReadStru, ReadAtomPositionsWarning5) remove("read_atom_positions.warn"); } #endif -TEST_F(UcellTest, ReadOrbFileWarning) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Read"]; - ucell = utp.SetUcellInfo(); - std::string orb_file = "./support/CC.orb"; - std::ofstream ofs_running; - ofs_running.open("tmp_readorbfilewarning"); - testing::internal::CaptureStdout(); - bool result = unitcell::read_orb_file(0, orb_file, ofs_running, &(ucell->atoms[0])); - output = testing::internal::GetCapturedStdout(); - ofs_running << output << std::endl; - EXPECT_FALSE(result); - EXPECT_THAT(output, testing::HasSubstr("Element index 1")); - EXPECT_THAT(output, testing::HasSubstr("orbital file: ./support/CC.orb")); - ofs_running.close(); - remove("tmp_readorbfilewarning"); -} From cda48b0675dc0d647d3257bdbd697df462c696fb Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 12:45:01 +0800 Subject: [PATCH 06/21] Test: move compare_atom_labels/print_unitcell_pseudo tests to readpp suite CompareAatomLabel and PrintUnitcellPseudo exercise read_pp_ucell.cpp, so move them from MODULE_CELL_unitcell_test into the existing MODULE_CELL_unitcell_test_readpp target. The rebuilt cell needed by PrintUnitcellPseudo uses the C1H2-Index prepare entry. Drop the now unused read_pp_ucell.h include from unitcell_test.cpp. --- source/source_cell/test/unitcell_test.cpp | 47 ----------------- .../source_cell/test/unitcell_test_readpp.cpp | 51 +++++++++++++++++++ 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index 588d62b2dff..72158df889d 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -1,7 +1,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "source_cell/read_pp_ucell.h" #include "source_cell/read_stru.h" #include "source_cell/cell_tools.h" #include "source_cell/print_cell.h" @@ -65,8 +64,6 @@ Magnetism::~Magnetism() * - PrintTauDirect * - PrintTauCartesian * - print_tau(): print atomic coordinates, magmom and initial velocities - * - PrintUnitcellPseudo - * - Actually an integrated function to call unitcell::print_cell and Atom::print_Atom * - UpdateVel * - update_vel(const ModuleBase::Vector3* vel_in) * - ReadAtomSpecies @@ -218,27 +215,6 @@ TEST_F(UcellTest, Setup) } } -TEST_F(UcellDeathTest, CompareAatomLabel) -{ - std::string stru_label[] - = {"Ag", "Ag", "Ag", "47", "47", "47", "Silver", "Silver", "Silver", "Ag", "Ag", "Ag", "Ag_empty"}; - std::string pseudo_label[] - = {"Ag", "47", "Silver", "Ag", "47", "Silver", "Ag", "47", "Silver", "Ag1", "ag", "ag_locpsp", "Ag"}; - for (int it = 0; it < 12; it++) - { - unitcell::compare_atom_labels(stru_label[it], pseudo_label[it]); - } - stru_label[0] = "Fe"; - pseudo_label[0] = "O"; - std::string atom_label_in_orbtial = "atom label in orbital file "; - std::string mismatch_with_pseudo = " mismatch with pseudo file of "; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::compare_atom_labels(stru_label[0], pseudo_label[0]), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, - testing::HasSubstr(atom_label_in_orbtial + stru_label[0] + mismatch_with_pseudo + pseudo_label[0])); -} - TEST_F(UcellTest, RemakeCell) { std::vector latname_in = {"sc", @@ -740,29 +716,6 @@ TEST_F(UcellTest, PrintCell) remove("printcell.log"); } -TEST_F(UcellTest, PrintUnitcellPseudo) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; - ucell = utp.SetUcellInfo(); - std::string fn = "printcell.log"; - unitcell::print_unitcell_pseudo(fn, *ucell); - std::ifstream ifs; - ifs.open("printcell.log"); - std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("latName = bcc")); - EXPECT_THAT(str, testing::HasSubstr("ntype = 2")); - EXPECT_THAT(str, testing::HasSubstr("nat = 3")); - EXPECT_THAT(str, testing::HasSubstr("GGT :")); - EXPECT_THAT(str, testing::HasSubstr("omega = 6748.33")); - EXPECT_THAT(str, testing::HasSubstr("label = C")); - EXPECT_THAT(str, testing::HasSubstr("mass = 12")); - EXPECT_THAT(str, testing::HasSubstr("atom_position(cartesian) Dimension = 1")); - EXPECT_THAT(str, testing::HasSubstr("label = H")); - EXPECT_THAT(str, testing::HasSubstr("mass = 1")); - EXPECT_THAT(str, testing::HasSubstr("atom_position(cartesian) Dimension = 2")); - remove("printcell.log"); -} - // Comments and suggestions on the refactor of UnitCell class // NOTE: PrintSTRU tests have been moved to test_print_cell.cpp diff --git a/source/source_cell/test/unitcell_test_readpp.cpp b/source/source_cell/test/unitcell_test_readpp.cpp index 7c53673e045..abd7bb41aa3 100644 --- a/source/source_cell/test/unitcell_test_readpp.cpp +++ b/source/source_cell/test/unitcell_test_readpp.cpp @@ -7,6 +7,7 @@ #include "source_cell/unitcell.h" #include "source_cell/cal_nelec_nband.h" #include "source_cell/read_pp_ucell.h" +#include #include #include #include "string.h" @@ -83,6 +84,10 @@ Magnetism::~Magnetism() { } * - calculate the total number of valence electrons from psp files * - CalNbands: unitcell::cal_nbands() * - calculate the number of bands + * - CompareAatomLabel + * - compare_atom_labels(): accept equivalent element names and quit on mismatch + * - PrintUnitcellPseudo + * - print_unitcell_pseudo(): print basic cell and atom info to a pseudo log */ class UcellTest : public ::testing::Test { @@ -763,6 +768,52 @@ TEST_F(UcellTest, CalNbandsGaussWarning) EXPECT_THAT(output, testing::HasSubstr("for smearing, num. of bands > num. of occupied bands")); } +TEST_F(UcellDeathTest, CompareAatomLabel) +{ + std::string stru_label[] + = {"Ag", "Ag", "Ag", "47", "47", "47", "Silver", "Silver", "Silver", "Ag", "Ag", "Ag", "Ag_empty"}; + std::string pseudo_label[] + = {"Ag", "47", "Silver", "Ag", "47", "Silver", "Ag", "47", "Silver", "Ag1", "ag", "ag_locpsp", "Ag"}; + for (int it = 0; it < 12; it++) + { + unitcell::compare_atom_labels(stru_label[it], pseudo_label[it]); + } + stru_label[0] = "Fe"; + pseudo_label[0] = "O"; + std::string atom_label_in_orbtial = "atom label in orbital file "; + std::string mismatch_with_pseudo = " mismatch with pseudo file of "; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::compare_atom_labels(stru_label[0], pseudo_label[0]), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, + testing::HasSubstr(atom_label_in_orbtial + stru_label[0] + mismatch_with_pseudo + pseudo_label[0])); +} + +TEST_F(UcellTest, PrintUnitcellPseudo) +{ + // The fixture builds the C1H2-Read cell in SetUp; this test needs the + // C1H2-Index variant, so rebuild the cell here. + UcellTestPrepare utp_index = UcellTestLib["C1H2-Index"]; + ucell = utp_index.SetUcellInfo(); + std::string fn = "printcell.log"; + unitcell::print_unitcell_pseudo(fn, *ucell); + std::ifstream ifs; + ifs.open("printcell.log"); + std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("latName = bcc")); + EXPECT_THAT(str, testing::HasSubstr("ntype = 2")); + EXPECT_THAT(str, testing::HasSubstr("nat = 3")); + EXPECT_THAT(str, testing::HasSubstr("GGT :")); + EXPECT_THAT(str, testing::HasSubstr("omega = 6748.33")); + EXPECT_THAT(str, testing::HasSubstr("label = C")); + EXPECT_THAT(str, testing::HasSubstr("mass = 12")); + EXPECT_THAT(str, testing::HasSubstr("atom_position(cartesian) Dimension = 1")); + EXPECT_THAT(str, testing::HasSubstr("label = H")); + EXPECT_THAT(str, testing::HasSubstr("mass = 1")); + EXPECT_THAT(str, testing::HasSubstr("atom_position(cartesian) Dimension = 2")); + remove("printcell.log"); +} + #ifdef __MPI #include "mpi.h" int main(int argc, char** argv) { From 27ae2da15b403c90de2dddab2ffa09c174817d89 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 12:55:58 +0800 Subject: [PATCH 07/21] Test: split update_cell tests out of MODULE_CELL_unitcell_test Move RemakeCell, RemakeCellWarnings, PeriodicBoundaryAdjustment2 and UpdateVel to the new test_update_cell.cpp / MODULE_CELL_update_cell_test target. The commented-out PeriodicBoundaryAdjustment1 block moves as well and stays commented. update_cell.cpp is already part of the test-only cell_info library. Drop the now unused update_cell.h include from unitcell_test.cpp. --- source/source_cell/test/CMakeLists.txt | 6 + source/source_cell/test/test_update_cell.cpp | 383 +++++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 341 ----------------- 3 files changed, 389 insertions(+), 341 deletions(-) create mode 100644 source/source_cell/test/test_update_cell.cpp diff --git a/source/source_cell/test/CMakeLists.txt b/source/source_cell/test/CMakeLists.txt index fd80397a14a..b1e0bc66f14 100644 --- a/source/source_cell/test/CMakeLists.txt +++ b/source/source_cell/test/CMakeLists.txt @@ -188,6 +188,12 @@ AddTest( SOURCES test_read_orb.cpp ) +AddTest( + TARGET MODULE_CELL_update_cell_test + LIBS base device cell_info + SOURCES test_update_cell.cpp +) + AddTest( TARGET MODULE_CELL_print_cell_test LIBS base device cell_info symmetry diff --git a/source/source_cell/test/test_update_cell.cpp b/source/source_cell/test/test_update_cell.cpp new file mode 100644 index 00000000000..df888e989c8 --- /dev/null +++ b/source/source_cell/test/test_update_cell.cpp @@ -0,0 +1,383 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include + +#include "source_base/global_variable.h" +#include "source_base/mathzone.h" +#include "source_cell/unitcell.h" +#include "source_cell/update_cell.h" +#include "prepare_unitcell.h" + +// The test-only cell_info object library does not contain magnetism.cpp, +// so the Magnetism constructor/destructor must be provided locally. +Magnetism::Magnetism() +{ + this->tot_mag = 0.0; + this->abs_mag = 0.0; +} +Magnetism::~Magnetism() +{ +} + +/************************************************ + * unit test of update_cell.cpp + ***********************************************/ + +/** + * - Tested Functions: + * - RemakeCell + * - remake_cell(): rebuild cell according to its latName + * - RemakeCellWarnings + * - remake_cell(): deliver warnings when find wrong latname or cos12 + * - PeriodicBoundaryAdjustment + * - periodic_boundary_adjustment(): move atoms inside the unitcell after relaxation + * - UpdateVel + * - update_vel(const ModuleBase::Vector3* vel_in) + */ + +class UpdateCellTest : public ::testing::Test +{ + protected: + std::unique_ptr ucell{new UnitCell}; + std::string output; +}; + +using UpdateCellDeathTest = UpdateCellTest; + + +TEST_F(UpdateCellTest, RemakeCell) +{ + std::vector latname_in = {"sc", + "fcc", + "bcc", + "hexagonal", + "trigonal", + "st", + "bct", + "so", + "baco", + "fco", + "bco", + "sm", + "bacm", + "triclinic"}; + for (int i = 0; i < latname_in.size(); ++i) + { + ucell->latvec.e11 = 10.0; + ucell->latvec.e12 = 0.00; + ucell->latvec.e13 = 0.00; + ucell->latvec.e21 = 0.00; + ucell->latvec.e22 = 10.0; + ucell->latvec.e23 = 0.00; + ucell->latvec.e31 = 0.00; + ucell->latvec.e32 = 0.00; + ucell->latvec.e33 = 10.0; + ucell->latName = latname_in[i]; + unitcell::remake_cell(ucell->lat); + if (latname_in[i] == "sc") + { + double celldm + = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, celldm); + } + else if (latname_in[i] == "fcc") + { + double celldm = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)) + / std::sqrt(2.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, -celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, -celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 0.0); + } + else if (latname_in[i] == "bcc") + { + double celldm = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)) + / std::sqrt(3.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, -celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, -celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, -celldm); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, celldm); + } + else if (latname_in[i] == "hexagonal") + { + double celldm1 + = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + double celldm3 + = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); + double mathfoo = sqrt(3.0) / 2.0; + EXPECT_DOUBLE_EQ(ucell->latvec.e11, celldm1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, -0.5 * celldm1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, celldm1 * mathfoo); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, celldm3); + } + else if (latname_in[i] == "trigonal") + { + double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); + double a1da2 = (ucell->latvec.e11 * ucell->latvec.e21 + ucell->latvec.e12 * ucell->latvec.e22 + + ucell->latvec.e13 * ucell->latvec.e23); + double cosgamma = a1da2 / (a1 * a2); + double tx = std::sqrt((1.0 - cosgamma) / 2.0); + double ty = std::sqrt((1.0 - cosgamma) / 6.0); + double tz = std::sqrt((1.0 + 2.0 * cosgamma) / 3.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1 * tx); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, -a1 * ty); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, a1 * tz); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 2.0 * a1 * ty); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, a1 * tz); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, -a1 * tx); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, -a1 * ty); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, a1 * tz); + } + else if (latname_in[i] == "st") + { + double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, a1); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3); + } + else if (latname_in[i] == "bct") + { + double d1 = std::abs(ucell->latvec.e11); + double d2 = std::abs(ucell->latvec.e13); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, -d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, -d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, -d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, d2); + } + else if (latname_in[i] == "so") + { + double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); + double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, a2); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3); + } + else if (latname_in[i] == "baco") + { + double d1 = std::abs(ucell->latvec.e11); + double d2 = std::abs(ucell->latvec.e22); + double d3 = std::abs(ucell->latvec.e33); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, -d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); + } + else if (latname_in[i] == "fco") + { + double d1 = std::abs(ucell->latvec.e11); + double d2 = std::abs(ucell->latvec.e22); + double d3 = std::abs(ucell->latvec.e33); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, d3); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); + } + else if (latname_in[i] == "bco") + { + double d1 = std::abs(ucell->latvec.e11); + double d2 = std::abs(ucell->latvec.e22); + double d3 = std::abs(ucell->latvec.e33); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, d3); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, -d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, d3); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, -d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, -d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); + } + else if (latname_in[i] == "sm") + { + double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); + double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); + double a1da2 = (ucell->latvec.e11 * ucell->latvec.e21 + ucell->latvec.e12 * ucell->latvec.e22 + + ucell->latvec.e13 * ucell->latvec.e23); + double cosgamma = a1da2 / (a1 * a2); + double d1 = a2 * cosgamma; + double d2 = a2 * std::sqrt(1.0 - cosgamma * cosgamma); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3); + } + else if (latname_in[i] == "bacm") + { + double d1 = std::abs(ucell->latvec.e11); + double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); + double d3 = std::abs(ucell->latvec.e13); + double cosgamma = ucell->latvec.e21 / a2; + double f1 = a2 * cosgamma; + double f2 = a2 * std::sqrt(1.0 - cosgamma * cosgamma); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, -d3); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, f1); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, f2); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, d1); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); + } + else if (latname_in[i] == "triclinic") + { + double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); + double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); + double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); + double a1da2 = (ucell->latvec.e11 * ucell->latvec.e21 + ucell->latvec.e12 * ucell->latvec.e22 + + ucell->latvec.e13 * ucell->latvec.e23); + double a1da3 = (ucell->latvec.e11 * ucell->latvec.e31 + ucell->latvec.e12 * ucell->latvec.e32 + + ucell->latvec.e13 * ucell->latvec.e33); + double a2da3 = (ucell->latvec.e21 * ucell->latvec.e31 + ucell->latvec.e22 * ucell->latvec.e32 + + ucell->latvec.e23 * ucell->latvec.e33); + double cosgamma = a1da2 / a1 / a2; + double singamma = std::sqrt(1.0 - cosgamma * cosgamma); + double cosbeta = a1da3 / a1 / a3; + double cosalpha = a2da3 / a2 / a3; + double d1 = std::sqrt(1.0 + 2.0 * cosgamma * cosbeta * cosalpha - cosgamma * cosgamma - cosbeta * cosbeta + - cosalpha * cosalpha) + / singamma; + EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); + EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e21, a2 * cosgamma); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, a2 * singamma); + EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e31, a3 * cosbeta); + EXPECT_DOUBLE_EQ(ucell->latvec.e32, a3 * (cosalpha - cosbeta * cosgamma) / singamma); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3 * d1); + } + } +} + +TEST_F(UpdateCellDeathTest, RemakeCellWarnings) +{ + std::vector latname_in = {"user_defined_lattice", "trigonal", "bacm", "triclinic", "arbitrary"}; + for (int i = 0; i < latname_in.size(); ++i) + { + ucell->latvec.e11 = 10.0; + ucell->latvec.e12 = 0.00; + ucell->latvec.e13 = 0.00; + ucell->latvec.e21 = 10.0; + ucell->latvec.e22 = 0.00; + ucell->latvec.e23 = 0.00; + ucell->latvec.e31 = 0.00; + ucell->latvec.e32 = 0.00; + ucell->latvec.e33 = 10.0; + ucell->latName = latname_in[i]; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::remake_cell(ucell->lat), ::testing::ExitedWithCode(1), ""); + std::string output = testing::internal::GetCapturedStdout(); + if (latname_in[i] == "user_defined_lattice") + { + EXPECT_THAT(output, testing::HasSubstr("to use fixed_ibrav, latname must be provided")); + } + else if (latname_in[i] == "trigonal" || latname_in[i] == "bacm" || latname_in[i] == "triclinic") + { + EXPECT_THAT(output, testing::HasSubstr("wrong cos12!")); + } + else + { + EXPECT_THAT(output, testing::HasSubstr("latname type not supported!")); + } + } +} + +// mohan comment out 2025-07-14 +/* +TEST_F(UpdateCellDeathTest, PeriodicBoundaryAdjustment1) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-PBA"]; + ucell = utp.SetUcellInfo(); + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::periodic_boundary_adjustment( + ucell->atoms,ucell->latvec,ucell->ntype), + ::testing::ExitedWithCode(1), ""); + std::string output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("the movement of atom is larger than the length of cell")); +} +*/ + +TEST_F(UpdateCellTest, PeriodicBoundaryAdjustment2) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + EXPECT_NO_THROW(unitcell::periodic_boundary_adjustment( + ucell->atoms,ucell->latvec,ucell->ntype)); +} + +TEST_F(UpdateCellTest, UpdateVel) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + ModuleBase::Vector3* vel_in = new ModuleBase::Vector3[ucell->nat]; + for (int iat = 0; iat < ucell->nat; ++iat) + { + vel_in[iat].set(iat * 0.1, iat * 0.1, iat * 0.1); + } + unitcell::update_vel(vel_in,ucell->ntype,ucell->nat,ucell->atoms); + for (int iat = 0; iat < ucell->nat; ++iat) + { + EXPECT_DOUBLE_EQ(vel_in[iat].x, 0.1 * iat); + EXPECT_DOUBLE_EQ(vel_in[iat].y, 0.1 * iat); + EXPECT_DOUBLE_EQ(vel_in[iat].z, 0.1 * iat); + } + delete[] vel_in; +} + diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index 72158df889d..ef54b87a9b8 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -9,7 +9,6 @@ #include "source_base/global_variable.h" #include "source_base/mathzone.h" #include "prepare_unitcell.h" -#include "source_cell/update_cell.h" #include "source_cell/read_stru.h" #include #include @@ -36,10 +35,6 @@ Magnetism::~Magnetism() * - Setup: * - setup_from_input(): to set latname, ntype, lmaxmax, init_vel, and lc * - if_cell_can_change(): judge if any lattice vector can change - * - RemakeCell - * - remake_cell(): rebuild cell according to its latName - * - RemakeCellWarnings - * - remake_cell(): deliver warnings when find wrong latname or cos12 * - Index * - set_iat2iait(): set index relations in two arrays of Unitcell: iat2it[nat], iat2ia[nat] * - iat2iait(): depends on the above function, but can find both ia & it from iat @@ -55,8 +50,6 @@ Magnetism::~Magnetism() * - check_tau(): check if any "two atoms are too close" * - SelectiveDynamics * - if_atoms_can_move():it is true if any coordinates of any atom can move, i.e. mbl = 1 - * - PeriodicBoundaryAdjustment - * - periodic_boundary_adjustment(): move atoms inside the unitcell after relaxation * - PrintCell * - print_cell(ofs): print basic cell info into ofs * - PrintSTRU @@ -64,8 +57,6 @@ Magnetism::~Magnetism() * - PrintTauDirect * - PrintTauCartesian * - print_tau(): print atomic coordinates, magmom and initial velocities - * - UpdateVel - * - update_vel(const ModuleBase::Vector3* vel_in) * - ReadAtomSpecies * - read_atom_species(): a successful case * - ReadAtomSpeciesWarning1 @@ -215,296 +206,6 @@ TEST_F(UcellTest, Setup) } } -TEST_F(UcellTest, RemakeCell) -{ - std::vector latname_in = {"sc", - "fcc", - "bcc", - "hexagonal", - "trigonal", - "st", - "bct", - "so", - "baco", - "fco", - "bco", - "sm", - "bacm", - "triclinic"}; - for (int i = 0; i < latname_in.size(); ++i) - { - ucell->latvec.e11 = 10.0; - ucell->latvec.e12 = 0.00; - ucell->latvec.e13 = 0.00; - ucell->latvec.e21 = 0.00; - ucell->latvec.e22 = 10.0; - ucell->latvec.e23 = 0.00; - ucell->latvec.e31 = 0.00; - ucell->latvec.e32 = 0.00; - ucell->latvec.e33 = 10.0; - ucell->latName = latname_in[i]; - unitcell::remake_cell(ucell->lat); - if (latname_in[i] == "sc") - { - double celldm - = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, celldm); - } - else if (latname_in[i] == "fcc") - { - double celldm = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)) - / std::sqrt(2.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, -celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, -celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 0.0); - } - else if (latname_in[i] == "bcc") - { - double celldm = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)) - / std::sqrt(3.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, -celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, -celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, -celldm); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, celldm); - } - else if (latname_in[i] == "hexagonal") - { - double celldm1 - = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - double celldm3 - = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); - double mathfoo = sqrt(3.0) / 2.0; - EXPECT_DOUBLE_EQ(ucell->latvec.e11, celldm1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, -0.5 * celldm1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, celldm1 * mathfoo); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, celldm3); - } - else if (latname_in[i] == "trigonal") - { - double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); - double a1da2 = (ucell->latvec.e11 * ucell->latvec.e21 + ucell->latvec.e12 * ucell->latvec.e22 - + ucell->latvec.e13 * ucell->latvec.e23); - double cosgamma = a1da2 / (a1 * a2); - double tx = std::sqrt((1.0 - cosgamma) / 2.0); - double ty = std::sqrt((1.0 - cosgamma) / 6.0); - double tz = std::sqrt((1.0 + 2.0 * cosgamma) / 3.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1 * tx); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, -a1 * ty); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, a1 * tz); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 2.0 * a1 * ty); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, a1 * tz); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, -a1 * tx); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, -a1 * ty); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, a1 * tz); - } - else if (latname_in[i] == "st") - { - double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, a1); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3); - } - else if (latname_in[i] == "bct") - { - double d1 = std::abs(ucell->latvec.e11); - double d2 = std::abs(ucell->latvec.e13); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, -d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, -d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, -d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, d2); - } - else if (latname_in[i] == "so") - { - double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); - double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, a2); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3); - } - else if (latname_in[i] == "baco") - { - double d1 = std::abs(ucell->latvec.e11); - double d2 = std::abs(ucell->latvec.e22); - double d3 = std::abs(ucell->latvec.e33); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, -d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); - } - else if (latname_in[i] == "fco") - { - double d1 = std::abs(ucell->latvec.e11); - double d2 = std::abs(ucell->latvec.e22); - double d3 = std::abs(ucell->latvec.e33); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, d3); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); - } - else if (latname_in[i] == "bco") - { - double d1 = std::abs(ucell->latvec.e11); - double d2 = std::abs(ucell->latvec.e22); - double d3 = std::abs(ucell->latvec.e33); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, d3); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, -d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, d3); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, -d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, -d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); - } - else if (latname_in[i] == "sm") - { - double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); - double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); - double a1da2 = (ucell->latvec.e11 * ucell->latvec.e21 + ucell->latvec.e12 * ucell->latvec.e22 - + ucell->latvec.e13 * ucell->latvec.e23); - double cosgamma = a1da2 / (a1 * a2); - double d1 = a2 * cosgamma; - double d2 = a2 * std::sqrt(1.0 - cosgamma * cosgamma); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, d2); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3); - } - else if (latname_in[i] == "bacm") - { - double d1 = std::abs(ucell->latvec.e11); - double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); - double d3 = std::abs(ucell->latvec.e13); - double cosgamma = ucell->latvec.e21 / a2; - double f1 = a2 * cosgamma; - double f2 = a2 * std::sqrt(1.0 - cosgamma * cosgamma); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, -d3); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, f1); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, f2); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, d1); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, d3); - } - else if (latname_in[i] == "triclinic") - { - double a1 = std::sqrt(pow(ucell->latvec.e11, 2) + pow(ucell->latvec.e12, 2) + pow(ucell->latvec.e13, 2)); - double a2 = std::sqrt(pow(ucell->latvec.e21, 2) + pow(ucell->latvec.e22, 2) + pow(ucell->latvec.e23, 2)); - double a3 = std::sqrt(pow(ucell->latvec.e31, 2) + pow(ucell->latvec.e32, 2) + pow(ucell->latvec.e33, 2)); - double a1da2 = (ucell->latvec.e11 * ucell->latvec.e21 + ucell->latvec.e12 * ucell->latvec.e22 - + ucell->latvec.e13 * ucell->latvec.e23); - double a1da3 = (ucell->latvec.e11 * ucell->latvec.e31 + ucell->latvec.e12 * ucell->latvec.e32 - + ucell->latvec.e13 * ucell->latvec.e33); - double a2da3 = (ucell->latvec.e21 * ucell->latvec.e31 + ucell->latvec.e22 * ucell->latvec.e32 - + ucell->latvec.e23 * ucell->latvec.e33); - double cosgamma = a1da2 / a1 / a2; - double singamma = std::sqrt(1.0 - cosgamma * cosgamma); - double cosbeta = a1da3 / a1 / a3; - double cosalpha = a2da3 / a2 / a3; - double d1 = std::sqrt(1.0 + 2.0 * cosgamma * cosbeta * cosalpha - cosgamma * cosgamma - cosbeta * cosbeta - - cosalpha * cosalpha) - / singamma; - EXPECT_DOUBLE_EQ(ucell->latvec.e11, a1); - EXPECT_DOUBLE_EQ(ucell->latvec.e12, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e13, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e21, a2 * cosgamma); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, a2 * singamma); - EXPECT_DOUBLE_EQ(ucell->latvec.e23, 0.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e31, a3 * cosbeta); - EXPECT_DOUBLE_EQ(ucell->latvec.e32, a3 * (cosalpha - cosbeta * cosgamma) / singamma); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, a3 * d1); - } - } -} - -TEST_F(UcellDeathTest, RemakeCellWarnings) -{ - std::vector latname_in = {"user_defined_lattice", "trigonal", "bacm", "triclinic", "arbitrary"}; - for (int i = 0; i < latname_in.size(); ++i) - { - ucell->latvec.e11 = 10.0; - ucell->latvec.e12 = 0.00; - ucell->latvec.e13 = 0.00; - ucell->latvec.e21 = 10.0; - ucell->latvec.e22 = 0.00; - ucell->latvec.e23 = 0.00; - ucell->latvec.e31 = 0.00; - ucell->latvec.e32 = 0.00; - ucell->latvec.e33 = 10.0; - ucell->latName = latname_in[i]; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::remake_cell(ucell->lat), ::testing::ExitedWithCode(1), ""); - std::string output = testing::internal::GetCapturedStdout(); - if (latname_in[i] == "user_defined_lattice") - { - EXPECT_THAT(output, testing::HasSubstr("to use fixed_ibrav, latname must be provided")); - } - else if (latname_in[i] == "trigonal" || latname_in[i] == "bacm" || latname_in[i] == "triclinic") - { - EXPECT_THAT(output, testing::HasSubstr("wrong cos12!")); - } - else - { - EXPECT_THAT(output, testing::HasSubstr("latname type not supported!")); - } - } -} - TEST_F(UcellTest, Index) { UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; @@ -674,29 +375,6 @@ TEST_F(UcellTest, SelectiveDynamics) } -// mohan comment out 2025-07-14 -/* -TEST_F(UcellDeathTest, PeriodicBoundaryAdjustment1) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-PBA"]; - ucell = utp.SetUcellInfo(); - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::periodic_boundary_adjustment( - ucell->atoms,ucell->latvec,ucell->ntype), - ::testing::ExitedWithCode(1), ""); - std::string output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr("the movement of atom is larger than the length of cell")); -} -*/ - -TEST_F(UcellTest, PeriodicBoundaryAdjustment2) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; - ucell = utp.SetUcellInfo(); - EXPECT_NO_THROW(unitcell::periodic_boundary_adjustment( - ucell->atoms,ucell->latvec,ucell->ntype)); -} - TEST_F(UcellTest, PrintCell) { UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; @@ -766,25 +444,6 @@ TEST_F(UcellTest, PrintTauCartesian) remove("print_tau_Cartesian"); } -TEST_F(UcellTest, UpdateVel) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; - ucell = utp.SetUcellInfo(); - ModuleBase::Vector3* vel_in = new ModuleBase::Vector3[ucell->nat]; - for (int iat = 0; iat < ucell->nat; ++iat) - { - vel_in[iat].set(iat * 0.1, iat * 0.1, iat * 0.1); - } - unitcell::update_vel(vel_in,ucell->ntype,ucell->nat,ucell->atoms); - for (int iat = 0; iat < ucell->nat; ++iat) - { - EXPECT_DOUBLE_EQ(vel_in[iat].x, 0.1 * iat); - EXPECT_DOUBLE_EQ(vel_in[iat].y, 0.1 * iat); - EXPECT_DOUBLE_EQ(vel_in[iat].z, 0.1 * iat); - } - delete[] vel_in; -} - #ifdef __LCAO class UcellTestReadStru : public ::testing::Test { From 974ba84649ef63c51e10911b87d3c3c45beece0c Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 13:18:52 +0800 Subject: [PATCH 08/21] Test: move print_cell/print_tau tests into MODULE_CELL_print_cell_test PrintCell, PrintTauDirect and PrintTauCartesian exercise print_cell.cpp, the same source as the PrintSTRU tests already living in test_print_cell.cpp. Move them into that file/target and reuse the PrintCellTest fixture. Drop the now unused print_cell.h include and the stale PrintSTRU navigation comment from unitcell_test.cpp. --- source/source_cell/test/test_print_cell.cpp | 65 +++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 77 --------------------- 2 files changed, 65 insertions(+), 77 deletions(-) diff --git a/source/source_cell/test/test_print_cell.cpp b/source/source_cell/test/test_print_cell.cpp index 9c6de5e94da..d9277fb4ae8 100644 --- a/source/source_cell/test/test_print_cell.cpp +++ b/source/source_cell/test/test_print_cell.cpp @@ -280,3 +280,68 @@ TEST_F(PrintCellTest, PrintSTRU_with_force) ifs.close(); remove(fn.c_str()); } + +TEST_F(PrintCellTest, PrintCell) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + std::ofstream ofs; + ofs.open("printcell.log"); + unitcell::print_cell(*ucell, ofs); + ofs.close(); + std::ifstream ifs; + ifs.open("printcell.log"); + std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("latName = bcc")); + EXPECT_THAT(str, testing::HasSubstr("ntype = 2")); + EXPECT_THAT(str, testing::HasSubstr("nat = 3")); + EXPECT_THAT(str, testing::HasSubstr("GGT :")); + EXPECT_THAT(str, testing::HasSubstr("omega = 6748.33")); + remove("printcell.log"); +} +TEST_F(PrintCellTest, PrintTauDirect) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + EXPECT_EQ(ucell->Coordinate, "Direct"); + + // open a file + std::ofstream ofs("print_tau_direct"); + unitcell::print_tau(ucell->atoms,ucell->Coordinate,ucell->ntype,ucell->lat0,ofs); + ofs.close(); + + // readin the data + std::ifstream ifs; + ifs.open("print_tau_direct"); + std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("DIRECT COORDINATES")); + EXPECT_THAT(str, testing::HasSubstr(" C 0.100000000000 0.100000000000 0.100000000000 0.0000")); + EXPECT_THAT(str, testing::HasSubstr(" H 0.150000000000 0.150000000000 0.150000000000 0.0000")); + ifs.close(); + + remove("print_tau_direct"); +} + +TEST_F(PrintCellTest, PrintTauCartesian) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Cartesian"]; + ucell = utp.SetUcellInfo(); + EXPECT_EQ(ucell->Coordinate, "Cartesian"); + + // open a file + std::ofstream ofs("print_tau_Cartesian"); + unitcell::print_tau(ucell->atoms,ucell->Coordinate,ucell->ntype,ucell->lat0,ofs); + ofs.close(); + + // readin the data + std::ifstream ifs; + ifs.open("print_tau_Cartesian"); + std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("CARTESIAN COORDINATES")); + EXPECT_THAT(str, testing::HasSubstr(" C 1.000000000000 1.000000000000 1.000000000000 0.0000")); + EXPECT_THAT(str, testing::HasSubstr(" H 1.500000000000 1.500000000000 1.500000000000 0.0000")); + ifs.close(); + + // remove the file + remove("print_tau_Cartesian"); +} diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index ef54b87a9b8..c5013bf2676 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -3,7 +3,6 @@ #include "source_cell/read_stru.h" #include "source_cell/cell_tools.h" -#include "source_cell/print_cell.h" #include "memory" #include "source_cell/read_stru.h" #include "source_base/global_variable.h" @@ -50,13 +49,6 @@ Magnetism::~Magnetism() * - check_tau(): check if any "two atoms are too close" * - SelectiveDynamics * - if_atoms_can_move():it is true if any coordinates of any atom can move, i.e. mbl = 1 - * - PrintCell - * - print_cell(ofs): print basic cell info into ofs - * - PrintSTRU - * - print_stru_file(): print STRU file of ABACUS - * - PrintTauDirect - * - PrintTauCartesian - * - print_tau(): print atomic coordinates, magmom and initial velocities * - ReadAtomSpecies * - read_atom_species(): a successful case * - ReadAtomSpeciesWarning1 @@ -375,75 +367,6 @@ TEST_F(UcellTest, SelectiveDynamics) } -TEST_F(UcellTest, PrintCell) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; - ucell = utp.SetUcellInfo(); - std::ofstream ofs; - ofs.open("printcell.log"); - unitcell::print_cell(*ucell, ofs); - ofs.close(); - std::ifstream ifs; - ifs.open("printcell.log"); - std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("latName = bcc")); - EXPECT_THAT(str, testing::HasSubstr("ntype = 2")); - EXPECT_THAT(str, testing::HasSubstr("nat = 3")); - EXPECT_THAT(str, testing::HasSubstr("GGT :")); - EXPECT_THAT(str, testing::HasSubstr("omega = 6748.33")); - remove("printcell.log"); -} - -// Comments and suggestions on the refactor of UnitCell class -// NOTE: PrintSTRU tests have been moved to test_print_cell.cpp - -TEST_F(UcellTest, PrintTauDirect) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; - ucell = utp.SetUcellInfo(); - EXPECT_EQ(ucell->Coordinate, "Direct"); - - // open a file - std::ofstream ofs("print_tau_direct"); - unitcell::print_tau(ucell->atoms,ucell->Coordinate,ucell->ntype,ucell->lat0,ofs); - ofs.close(); - - // readin the data - std::ifstream ifs; - ifs.open("print_tau_direct"); - std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("DIRECT COORDINATES")); - EXPECT_THAT(str, testing::HasSubstr(" C 0.100000000000 0.100000000000 0.100000000000 0.0000")); - EXPECT_THAT(str, testing::HasSubstr(" H 0.150000000000 0.150000000000 0.150000000000 0.0000")); - ifs.close(); - - remove("print_tau_direct"); -} - -TEST_F(UcellTest, PrintTauCartesian) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-Cartesian"]; - ucell = utp.SetUcellInfo(); - EXPECT_EQ(ucell->Coordinate, "Cartesian"); - - // open a file - std::ofstream ofs("print_tau_Cartesian"); - unitcell::print_tau(ucell->atoms,ucell->Coordinate,ucell->ntype,ucell->lat0,ofs); - ofs.close(); - - // readin the data - std::ifstream ifs; - ifs.open("print_tau_Cartesian"); - std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("CARTESIAN COORDINATES")); - EXPECT_THAT(str, testing::HasSubstr(" C 1.000000000000 1.000000000000 1.000000000000 0.0000")); - EXPECT_THAT(str, testing::HasSubstr(" H 1.500000000000 1.500000000000 1.500000000000 0.0000")); - ifs.close(); - - // remove the file - remove("print_tau_Cartesian"); -} - #ifdef __LCAO class UcellTestReadStru : public ::testing::Test { From 016e69e66ea5125b348bf0da653177640550e1f6 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 14:18:02 +0800 Subject: [PATCH 09/21] Test: split cell_tools free-function tests out of MODULE_CELL_unitcell_test Add test_cell_tools.cpp / MODULE_CELL_cell_tools_test for the unitcell:: free functions in cell_tools.cpp: - new IfCellCanChange truth-table case over the lattice-axis flags - vector get_atomCounts/get_lnchiCounts cases split out of the mixed member/free GetAtomCounts and GetLnchiCounts tests (the member get_atom_Counts/get_lnchi_Counts map assertions stay in unitcell_test) - SelectiveDynamics (if_atoms_can_move) moved verbatim Setup keeps only the setup_from_input member assertions; its if_cell_can_change checks are now covered by the dedicated truth table. Drop the unused cell_tools.h include from unitcell_test.cpp. --- source/source_cell/test/CMakeLists.txt | 6 ++ source/source_cell/test/test_cell_tools.cpp | 91 +++++++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 34 +------- 3 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 source/source_cell/test/test_cell_tools.cpp diff --git a/source/source_cell/test/CMakeLists.txt b/source/source_cell/test/CMakeLists.txt index b1e0bc66f14..4a99c48dd03 100644 --- a/source/source_cell/test/CMakeLists.txt +++ b/source/source_cell/test/CMakeLists.txt @@ -194,6 +194,12 @@ AddTest( SOURCES test_update_cell.cpp ) +AddTest( + TARGET MODULE_CELL_cell_tools_test + LIBS base device cell_info + SOURCES test_cell_tools.cpp +) + AddTest( TARGET MODULE_CELL_print_cell_test LIBS base device cell_info symmetry diff --git a/source/source_cell/test/test_cell_tools.cpp b/source/source_cell/test/test_cell_tools.cpp new file mode 100644 index 00000000000..267849f2b57 --- /dev/null +++ b/source/source_cell/test/test_cell_tools.cpp @@ -0,0 +1,91 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include + +#include "source_base/global_variable.h" +#include "source_base/mathzone.h" +#include "source_cell/unitcell.h" +#include "source_cell/cell_tools.h" +#include "prepare_unitcell.h" + +// The test-only cell_info object library does not contain magnetism.cpp, +// so the Magnetism constructor/destructor must be provided locally. +Magnetism::Magnetism() +{ + this->tot_mag = 0.0; + this->abs_mag = 0.0; +} +Magnetism::~Magnetism() +{ +} + +/************************************************ + * unit test of cell_tools.cpp + ***********************************************/ + +/** + * - Tested Functions: + * - IfCellCanChange + * - if_cell_can_change(): truth table over the three lattice-axis flags + * - GetAtomCounts + * - get_atomCounts(): number of atoms per type as a vector + * - GetLnchiCounts + * - get_lnchiCounts(): number of chi functions per L per type + * - SelectiveDynamics + * - if_atoms_can_move(): true if any atom has a movable coordinate + */ + +class CellToolsTest : public ::testing::Test +{ + protected: + std::unique_ptr ucell{new UnitCell}; +}; + +TEST_F(CellToolsTest, IfCellCanChange) +{ + // Mirror the fixed_axes -> lat_axis_free mapping produced by + // UnitCell::setup_from_input: the cell can change whenever at least one + // lattice axis is free; only "abc" (all axes fixed) returns false. + std::vector> axes_free = { + {1, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 1, 0}, + {0, 0, 1}, {0, 1, 0}, {1, 0, 0}, {0, 0, 0}}; + for (int i = 0; i < 7; ++i) + { + EXPECT_TRUE(unitcell::if_cell_can_change(axes_free[i])); + } + EXPECT_FALSE(unitcell::if_cell_can_change(axes_free[7])); +} + +TEST_F(CellToolsTest, GetAtomCounts) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + ucell->set_iat2itia(); + std::vector atomCounts = unitcell::get_atomCounts(ucell->atoms, ucell->ntype); + EXPECT_EQ(atomCounts[0], 1); + EXPECT_EQ(atomCounts[1], 2); +} + +TEST_F(CellToolsTest, GetLnchiCounts) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + ucell->set_iat2itia(); + std::vector> lnchiCounts = unitcell::get_lnchiCounts(ucell->atoms, ucell->ntype); + EXPECT_EQ(lnchiCounts[0][0], 1); + EXPECT_EQ(lnchiCounts[0][1], 1); + EXPECT_EQ(lnchiCounts[0][2], 1); + EXPECT_EQ(lnchiCounts[1][0], 1); + EXPECT_EQ(lnchiCounts[1][1], 1); + EXPECT_EQ(lnchiCounts[1][2], 1); +} + +TEST_F(CellToolsTest, SelectiveDynamics) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-SD"]; + ucell = utp.SetUcellInfo(); + EXPECT_TRUE(unitcell::if_atoms_can_move(ucell->atoms, ucell->ntype)); +} diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index c5013bf2676..a3418c8bb3e 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -2,7 +2,6 @@ #include "gtest/gtest.h" #include "source_cell/read_stru.h" -#include "source_cell/cell_tools.h" #include "memory" #include "source_cell/read_stru.h" #include "source_base/global_variable.h" @@ -33,22 +32,19 @@ Magnetism::~Magnetism() * - UnitCell() and ~UnitCell() * - Setup: * - setup_from_input(): to set latname, ntype, lmaxmax, init_vel, and lc - * - if_cell_can_change(): judge if any lattice vector can change * - Index * - set_iat2iait(): set index relations in two arrays of Unitcell: iat2it[nat], iat2ia[nat] * - iat2iait(): depends on the above function, but can find both ia & it from iat * - ijat2iaitjajt(): find ia, it, ja, jt from ijat (ijat_max = nat*nat) * which collapses it, ia, jt, ja loop into a single loop * - GetAtomCounts - * - get_atomCounts(): get atomCounts, which is a map from atom type to atom number + * - get_atom_Counts(): get atomCounts, which is a map from atom type to atom number * - GetOrbitalCounts * - get_orbitalCounts(): get orbitalCounts, which is a map from atom type to orbital number * - CheckDTau * - check_dtau(): move all atomic coordinates into the first unitcell, i.e. in between [0,1) * - CheckTau * - check_tau(): check if any "two atoms are too close" - * - SelectiveDynamics - * - if_atoms_can_move():it is true if any coordinates of any atom can move, i.e. mbl = 1 * - ReadAtomSpecies * - read_atom_species(): a successful case * - ReadAtomSpeciesWarning1 @@ -144,56 +140,48 @@ TEST_F(UcellTest, Setup) EXPECT_EQ(ucell->lat_axis_free[0], 1); EXPECT_EQ(ucell->lat_axis_free[1], 1); EXPECT_EQ(ucell->lat_axis_free[2], 1); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "a") { EXPECT_EQ(ucell->lat_axis_free[0], 0); EXPECT_EQ(ucell->lat_axis_free[1], 1); EXPECT_EQ(ucell->lat_axis_free[2], 1); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "b") { EXPECT_EQ(ucell->lat_axis_free[0], 1); EXPECT_EQ(ucell->lat_axis_free[1], 0); EXPECT_EQ(ucell->lat_axis_free[2], 1); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "c") { EXPECT_EQ(ucell->lat_axis_free[0], 1); EXPECT_EQ(ucell->lat_axis_free[1], 1); EXPECT_EQ(ucell->lat_axis_free[2], 0); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "ab") { EXPECT_EQ(ucell->lat_axis_free[0], 0); EXPECT_EQ(ucell->lat_axis_free[1], 0); EXPECT_EQ(ucell->lat_axis_free[2], 1); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "ac") { EXPECT_EQ(ucell->lat_axis_free[0], 0); EXPECT_EQ(ucell->lat_axis_free[1], 1); EXPECT_EQ(ucell->lat_axis_free[2], 0); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "bc") { EXPECT_EQ(ucell->lat_axis_free[0], 1); EXPECT_EQ(ucell->lat_axis_free[1], 0); EXPECT_EQ(ucell->lat_axis_free[2], 0); - EXPECT_TRUE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } else if (fixed_axes_in[i] == "abc") { EXPECT_EQ(ucell->lat_axis_free[0], 0); EXPECT_EQ(ucell->lat_axis_free[1], 0); EXPECT_EQ(ucell->lat_axis_free[2], 0); - EXPECT_FALSE(unitcell::if_cell_can_change(ucell->lat_axis_free)); } } } @@ -259,10 +247,6 @@ TEST_F(UcellTest, GetAtomCounts) std::map atomCounts = ucell->get_atom_Counts(); EXPECT_EQ(atomCounts[0], 1); EXPECT_EQ(atomCounts[1], 2); - /// atomCounts as vector - std::vector atomCounts2 = unitcell::get_atomCounts(ucell->atoms, ucell->ntype); - EXPECT_EQ(atomCounts2[0], 1); - EXPECT_EQ(atomCounts2[1], 2); } TEST_F(UcellTest, GetOrbitalCounts) @@ -289,14 +273,6 @@ TEST_F(UcellTest, GetLnchiCounts) EXPECT_EQ(LnchiCounts[1][0], 1); EXPECT_EQ(LnchiCounts[1][1], 1); EXPECT_EQ(LnchiCounts[1][2], 1); - /// LnchiCounts as vector - std::vector> LnchiCounts2 = unitcell::get_lnchiCounts(ucell->atoms, ucell->ntype); - EXPECT_EQ(LnchiCounts2[0][0], 1); - EXPECT_EQ(LnchiCounts2[0][1], 1); - EXPECT_EQ(LnchiCounts2[0][2], 1); - EXPECT_EQ(LnchiCounts2[1][0], 1); - EXPECT_EQ(LnchiCounts2[1][1], 1); - EXPECT_EQ(LnchiCounts2[1][2], 1); } TEST_F(UcellTest, CheckDTau) @@ -359,14 +335,6 @@ TEST_F(UcellTest, CheckTauTrue) GlobalV::ofs_warning.close(); } -TEST_F(UcellTest, SelectiveDynamics) -{ - UcellTestPrepare utp = UcellTestLib["C1H2-SD"]; - ucell = utp.SetUcellInfo(); - EXPECT_TRUE(unitcell::if_atoms_can_move(ucell->atoms, ucell->ntype)); -} - - #ifdef __LCAO class UcellTestReadStru : public ::testing::Test { From 5d6950f8aa3930799a37bff970d27d234809f3a9 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 14:32:00 +0800 Subject: [PATCH 10/21] Test: split read_atom_species tests out of MODULE_CELL_unitcell_test Move the 7 STRU header-parsing cases (read_atom_species and read_lattice_constant, both in read_atom_species.cpp) to the new test_read_atom_species.cpp / MODULE_CELL_read_atom_species_test target, kept under the __LCAO guard as before. ReadAtomSpeciesWarning5 uses the death-test fixture alias; the rest use the new ReadAtomSpeciesTest fixture. Drop the stale doc entries from unitcell_test.cpp. --- source/source_cell/test/CMakeLists.txt | 6 + .../test/test_read_atom_species.cpp | 236 ++++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 182 -------------- 3 files changed, 242 insertions(+), 182 deletions(-) create mode 100644 source/source_cell/test/test_read_atom_species.cpp diff --git a/source/source_cell/test/CMakeLists.txt b/source/source_cell/test/CMakeLists.txt index 4a99c48dd03..740a6fbe242 100644 --- a/source/source_cell/test/CMakeLists.txt +++ b/source/source_cell/test/CMakeLists.txt @@ -200,6 +200,12 @@ AddTest( SOURCES test_cell_tools.cpp ) +AddTest( + TARGET MODULE_CELL_read_atom_species_test + LIBS base device cell_info + SOURCES test_read_atom_species.cpp +) + AddTest( TARGET MODULE_CELL_print_cell_test LIBS base device cell_info symmetry diff --git a/source/source_cell/test/test_read_atom_species.cpp b/source/source_cell/test/test_read_atom_species.cpp new file mode 100644 index 00000000000..104697b614d --- /dev/null +++ b/source/source_cell/test/test_read_atom_species.cpp @@ -0,0 +1,236 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include + +#include "source_base/global_variable.h" +#include "source_base/mathzone.h" +#include "source_cell/unitcell.h" +#include "source_cell/read_stru.h" + +// The test-only cell_info object library does not contain magnetism.cpp, +// so the Magnetism constructor/destructor must be provided locally. +Magnetism::Magnetism() +{ + this->tot_mag = 0.0; + this->abs_mag = 0.0; +} +Magnetism::~Magnetism() +{ +} + +#ifdef __LCAO +/************************************************ + * unit test of read_atom_species.cpp + ***********************************************/ + +/** + * - Tested Functions: + * - ReadAtomSpecies + * - read_atom_species(): a successful case + * - ReadAtomSpeciesWarning1 + * - read_atom_species(): unrecognized pseudopotential type + * - ReadAtomSpeciesLatName + * - read_lattice_constant(): all supported lattice names + * - ReadLatticeConstantWarning1 + * - read_lattice_constant(): lattice constant <= 0 + * - ReadLatticeConstantWarning2 + * - read_lattice_constant(): LATTICE_PARAMETERS without lattice type + * - ReadLatticeConstantWarning3 + * - read_lattice_constant(): LATTICE_VECTORS with explicit lattice type + * - ReadAtomSpeciesWarning5 + * - read_lattice_constant(): unsupported lattice name + */ + +class ReadAtomSpeciesTest : public ::testing::Test +{ + protected: + std::unique_ptr ucell{new UnitCell}; + std::string output; + void SetUp() override + { + ucell->ntype = 2; + ucell->pseudo_fn.resize(ucell->ntype); + ucell->pseudo_type.resize(ucell->ntype); + ucell->orbital_fn.resize(ucell->ntype); + } + void TearDown() override + { + ucell->orbital_fn.shrink_to_fit(); + } +}; + +using ReadAtomSpeciesDeathTest = ReadAtomSpeciesTest; + +TEST_F(ReadAtomSpeciesTest, ReadAtomSpecies) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species.tmp"); + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running, ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + ofs_running.close(); + ifa.close(); + remove("read_atom_species.tmp"); +} + +TEST_F(ReadAtomSpeciesTest, ReadAtomSpeciesWarning1) +{ + std::string fn = "./support/STRU_MgO_Warning1"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species.txt"); + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("unrecognized pseudopotential type.")); + ofs_running.close(); + ifa.close(); + //remove("read_atom_species.txt"); +} + +TEST_F(ReadAtomSpeciesTest, ReadLatticeConstantWarning1) +{ + std::string fn = "./support/STRU_MgO_Warning2"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species1.tmp"); + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("Lattice constant <= 0.0")); + ofs_running.close(); + ifa.close(); + remove("read_atom_species1.tmp"); +} + +TEST_F(ReadAtomSpeciesTest, ReadLatticeConstantWarning2) +{ + std::string fn = "./support/STRU_MgO_Warning3"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species.tmp"); + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, + testing::HasSubstr("do not use LATTICE_PARAMETERS without explicit specification of lattice type")); + ofs_running.close(); + ifa.close(); + remove("read_atom_species.tmp"); +} + +TEST_F(ReadAtomSpeciesTest, ReadLatticeConstantWarning3) +{ + std::string fn = "./support/STRU_MgO_Warning4"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species.tmp"); + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + ucell->latName = "bcc"; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, + testing::HasSubstr("do not use LATTICE_VECTORS along with explicit specification of lattice type")); + ofs_running.close(); + ifa.close(); + remove("read_atom_species.tmp"); +} + +TEST_F(ReadAtomSpeciesTest, ReadAtomSpeciesLatName) +{ + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + std::vector latName_in = {"sc", + "fcc", + "bcc", + "hexagonal", + "trigonal", + "st", + "bct", + "so", + "baco", + "fco", + "bco", + "sm", + "bacm", + "triclinic"}; + for (int i = 0; i < latName_in.size(); ++i) + { + std::string fn = "./support/STRU_MgO_LatName"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species.tmp"); + ucell->latName = latName_in[i]; + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + if (ucell->latName == "sc") + { + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 1.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 1.0); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 1.0); + } + ofs_running.close(); + ifa.close(); + remove("read_atom_species.tmp"); + } +} + +TEST_F(ReadAtomSpeciesDeathTest, ReadAtomSpeciesWarning5) +{ + std::string fn = "./support/STRU_MgO_LatName"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_species.tmp"); + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + ucell->latName = "arbitrary"; + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("latname not supported")); + ofs_running.close(); + ifa.close(); + remove("read_atom_species.tmp"); +} + +#endif // __LCAO diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index a3418c8bb3e..f78cf0239fc 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -45,20 +45,6 @@ Magnetism::~Magnetism() * - check_dtau(): move all atomic coordinates into the first unitcell, i.e. in between [0,1) * - CheckTau * - check_tau(): check if any "two atoms are too close" - * - ReadAtomSpecies - * - read_atom_species(): a successful case - * - ReadAtomSpeciesWarning1 - * - read_atom_species(): unrecognized pseudopotential type. - * - ReadAtomSpeciesWarning2 - * - read_atom_species(): lat0<=0.0 - * - ReadAtomSpeciesWarning3 - * - read_atom_species(): do not use LATTICE_PARAMETERS without explicit specification of lattice type - * - ReadAtomSpeciesWarning4 - * - read_atom_species():do not use LATTICE_VECTORS along with explicit specification of lattice type - * - ReadAtomSpeciesWarning5 - * - read_atom_species():latname not supported - * - ReadAtomSpeciesLatName - * - read_atom_species(): various latname * - ReadAtomPositionsS1 * - read_atom_positions(): spin 1 case * - ReadAtomPositionsS2 @@ -354,174 +340,6 @@ class UcellTestReadStru : public ::testing::Test } }; -TEST_F(UcellTestReadStru, ReadAtomSpecies) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species.tmp"); - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running, ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - ofs_running.close(); - ifa.close(); - remove("read_atom_species.tmp"); -} - -TEST_F(UcellTestReadStru, ReadAtomSpeciesWarning1) -{ - std::string fn = "./support/STRU_MgO_Warning1"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species.txt"); - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr("unrecognized pseudopotential type.")); - ofs_running.close(); - ifa.close(); - //remove("read_atom_species.txt"); -} - -TEST_F(UcellTestReadStru, ReadLatticeConstantWarning1) -{ - std::string fn = "./support/STRU_MgO_Warning2"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species1.tmp"); - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr("Lattice constant <= 0.0")); - ofs_running.close(); - ifa.close(); - remove("read_atom_species1.tmp"); -} - -TEST_F(UcellTestReadStru, ReadLatticeConstantWarning2) -{ - std::string fn = "./support/STRU_MgO_Warning3"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species.tmp"); - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, - testing::HasSubstr("do not use LATTICE_PARAMETERS without explicit specification of lattice type")); - ofs_running.close(); - ifa.close(); - remove("read_atom_species.tmp"); -} - -TEST_F(UcellTestReadStru, ReadLatticeConstantWarning3) -{ - std::string fn = "./support/STRU_MgO_Warning4"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species.tmp"); - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - ucell->latName = "bcc"; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, - testing::HasSubstr("do not use LATTICE_VECTORS along with explicit specification of lattice type")); - ofs_running.close(); - ifa.close(); - remove("read_atom_species.tmp"); -} - -TEST_F(UcellTestReadStru, ReadAtomSpeciesLatName) -{ - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - std::vector latName_in = {"sc", - "fcc", - "bcc", - "hexagonal", - "trigonal", - "st", - "bct", - "so", - "baco", - "fco", - "bco", - "sm", - "bacm", - "triclinic"}; - for (int i = 0; i < latName_in.size(); ++i) - { - std::string fn = "./support/STRU_MgO_LatName"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species.tmp"); - ucell->latName = latName_in[i]; - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - if (ucell->latName == "sc") - { - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 1.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 1.0); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 1.0); - } - ofs_running.close(); - ifa.close(); - remove("read_atom_species.tmp"); - } -} - -TEST_F(UcellDeathTest, ReadAtomSpeciesWarning5) -{ - std::string fn = "./support/STRU_MgO_LatName"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_species.tmp"); - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - ucell->latName = "arbitrary"; - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr("latname not supported")); - ofs_running.close(); - ifa.close(); - remove("read_atom_species.tmp"); -} - TEST_F(UcellTestReadStru, ReadAtomPositionsS1) { std::string fn = "./support/STRU_MgO"; From 55c4f5f063d35078da2e5001f9411e04a2a67a14 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 14:43:27 +0800 Subject: [PATCH 11/21] Test: split read_atoms tests out of MODULE_CELL_unitcell_test Move the 17 read_atom_positions cases (spin/coordinate modes plus the five warning cases) to the new test_read_atoms.cpp / MODULE_CELL_read_atoms_test target, kept under the __LCAO guard. This removes the LCAO block and UcellTestReadStru fixture from unitcell_test.cpp, which now holds only the 9 UnitCell member-function tests. Also de-duplicate the triple read_stru.h include. The 6 GlobalV references are migration-neutral (added = removed). --- source/source_cell/test/CMakeLists.txt | 6 + source/source_cell/test/test_read_atoms.cpp | 860 ++++++++++++++++++++ source/source_cell/test/unitcell_test.cpp | 826 ------------------- 3 files changed, 866 insertions(+), 826 deletions(-) create mode 100644 source/source_cell/test/test_read_atoms.cpp diff --git a/source/source_cell/test/CMakeLists.txt b/source/source_cell/test/CMakeLists.txt index 740a6fbe242..d681868cc7b 100644 --- a/source/source_cell/test/CMakeLists.txt +++ b/source/source_cell/test/CMakeLists.txt @@ -206,6 +206,12 @@ AddTest( SOURCES test_read_atom_species.cpp ) +AddTest( + TARGET MODULE_CELL_read_atoms_test + LIBS base device cell_info + SOURCES test_read_atoms.cpp +) + AddTest( TARGET MODULE_CELL_print_cell_test LIBS base device cell_info symmetry diff --git a/source/source_cell/test/test_read_atoms.cpp b/source/source_cell/test/test_read_atoms.cpp new file mode 100644 index 00000000000..d70c267e8ab --- /dev/null +++ b/source/source_cell/test/test_read_atoms.cpp @@ -0,0 +1,860 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include + +#include "source_base/global_variable.h" +#include "source_base/mathzone.h" +#include "source_cell/unitcell.h" +#include "source_cell/read_stru.h" + +// The test-only cell_info object library does not contain magnetism.cpp, +// so the Magnetism constructor/destructor must be provided locally. +Magnetism::Magnetism() +{ + this->tot_mag = 0.0; + this->abs_mag = 0.0; +} +Magnetism::~Magnetism() +{ +} + +#ifdef __LCAO +/************************************************ + * unit test of read_atoms.cpp + ***********************************************/ + +/** + * - Tested Functions: + * - ReadAtomPositionsS1 + * - read_atom_positions(): spin 1 case + * - ReadAtomPositionsS2 + * - read_atom_positions(): spin 2 case + * - ReadAtomPositionsS4Noncolin + * - read_atom_positions(): spin 4 noncolinear case + * - ReadAtomPositionsS4Colin + * - read_atom_positions(): spin 4 colinear case + * - ReadAtomPositionsC + * - read_atom_positions(): Cartesian coordinates + * - ReadAtomPositionsCA + * - read_atom_positions(): Cartesian_angstrom coordinates + * - ReadAtomPositionsCACXY + * - read_atom_positions(): Cartesian_angstrom_center_xy coordinates + * - ReadAtomPositionsCACXZ + * - read_atom_positions(): Cartesian_angstrom_center_xz coordinates + * - ReadAtomPositionsCACYZ + * - read_atom_positions(): Cartesian_angstrom_center_yz coordinates + * - ReadAtomPositionsCACXYZ + * - read_atom_positions(): Cartesian_angstrom_center_xyz coordinates + * - ReadAtomPositionsCAU + * - read_atom_positions(): Cartesian_au coordinates + * - ReadAtomPositionsAutosetMag + * - read_atom_positions(): zero-moment start with warning for nspin=4 + * - ReadAtomPositionsWarning1 + * - read_atom_positions(): unknown type of coordinates + * - ReadAtomPositionsWarning2 + * - read_atom_positions(): atomic label inconsistency between ATOM_POSITIONS + * and ATOM_SPECIES + * - ReadAtomPositionsWarning3 + * - read_atom_positions(): warning : atom number < 0 + * - ReadAtomPositionsWarning4 + * - read_atom_positions(): mismatch in atom number for atom type + * - ReadAtomPositionsWarning5 + * - read_atom_positions(): no atoms can move in MD simulations! + */ + +class ReadAtomsTest : public ::testing::Test +{ + protected: + std::unique_ptr ucell{new UnitCell}; + std::string output; + void SetUp() override + { + ucell->ntype = 2; + ucell->pseudo_fn.resize(ucell->ntype); + ucell->pseudo_type.resize(ucell->ntype); + ucell->orbital_fn.resize(ucell->ntype); + } + void TearDown() override + { + ucell->orbital_fn.shrink_to_fit(); + } +}; + +TEST_F(ReadAtomsTest, ReadAtomPositionsS1) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsS2) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 2; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsS4Noncolin) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 4; + const bool fixed_atoms = false; + const bool noncolin = true; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsS4Colin) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 4; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsC) +{ + std::string fn = "./support/STRU_MgO_c"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsCA) +{ + std::string fn = "./support/STRU_MgO_ca"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsCACXY) +{ + std::string fn = "./support/STRU_MgO_cacxy"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsCACXZ) +{ + std::string fn = "./support/STRU_MgO_cacxz"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsCACYZ) +{ + std::string fn = "./support/STRU_MgO_cacyz"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsCACXYZ) +{ + std::string fn = "./support/STRU_MgO_cacxyz"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsCAU) +{ + std::string fn = "./support/STRU_MgO_cau"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = true; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsAutosetMag) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + int nspin = 2; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + for (int it = 0; it < ucell->ntype; it++) + { + for (int ia = 0; ia < ucell->atoms[it].na; ia++) + { + EXPECT_DOUBLE_EQ(ucell->atoms[it].mag[ia], 1.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].z, 1.0); + } + } + // for nspin == 4 + // Issue #5939: nspin=4 with no mag in STRU no longer autosets (1,1,1); + // all moments stay zero and a warning is emitted instead. + nspin = 4; + testing::internal::CaptureStdout(); + unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0); + const std::string stdout_output = testing::internal::GetCapturedStdout(); + for (int it = 0; it < ucell->ntype; it++) + { + for (int ia = 0; ia < ucell->atoms[it].na; ia++) + { + EXPECT_DOUBLE_EQ(ucell->atoms[it].mag[ia], 0.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].x, 0.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].y, 0.0); + EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].z, 0.0); + } + } + // The zero-moment warning must reach both stdout and the running log. + EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), + std::string::npos); + ofs_running.flush(); + std::ifstream ifs_log("read_atom_positions.tmp"); + std::string log_content; + std::string log_line; + while (std::getline(ifs_log, log_line)) + { + log_content += log_line; + } + ifs_log.close(); + EXPECT_NE(log_content.find("no initial magnetization is set in STRU"), + std::string::npos); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsWarning1) +{ + std::string fn = "./support/STRU_MgO_WarningC1"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0)); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + // check warning file + std::ifstream ifs_tmp; + ifs_tmp.open("read_atom_positions.warn"); + std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("There are several options for you:")); + EXPECT_THAT(str, testing::HasSubstr("Direct")); + EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom")); + EXPECT_THAT(str, testing::HasSubstr("Cartesian_au")); + EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_xy")); + EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_xz")); + EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_yz")); + EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_xyz")); + ifs_tmp.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsWarning2) +{ + std::string fn = "./support/STRU_MgO_WarningC2"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0)); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + // check warning file + std::ifstream ifs_tmp; + ifs_tmp.open("read_atom_positions.warn"); + std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("Label read from ATOMIC_POSITIONS is Mo")); + EXPECT_THAT(str, testing::HasSubstr("Label from ATOMIC_SPECIES is Mg")); + ifs_tmp.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsWarning3) +{ + std::string fn = "./support/STRU_MgO_WarningC3"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_positions.tmp"); + GlobalV::ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, GlobalV::ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0)); + ofs_running.close(); + GlobalV::ofs_warning.close(); + ifa.close(); + // check warning file + std::ifstream ifs_tmp; + ifs_tmp.open("read_atom_positions.warn"); + std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("read_atom_positions warning : atom number < 0.")); + ifs_tmp.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsWarning4) +{ + std::string fn = "./support/STRU_MgO_WarningC4"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + std::ofstream ofs_warning; + ofs_running.open("read_atom_positions.tmp"); + ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->orbital_fn.resize(ucell->ntype); + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = false; + const bool noncolin = false; + const std::string calculation = "scf"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + testing::internal::CaptureStdout(); + EXPECT_EXIT(unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("read_atom_positions, mismatch in atom number for atom type: Mg")); + ofs_running.close(); + ofs_warning.close(); + ifa.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +TEST_F(ReadAtomsTest, ReadAtomPositionsWarning5) +{ + std::string fn = "./support/STRU_MgO"; + std::ifstream ifa(fn.c_str()); + std::ofstream ofs_running; + ofs_running.open("read_atom_positions.tmp"); + GlobalV::ofs_warning.open("read_atom_positions.warn"); + // mandatory preliminaries + ucell->ntype = 2; + ucell->atoms = new Atom[ucell->ntype]; + ucell->set_atom_flag = true; + const std::string basis_type = "lcao"; + const std::string orbital_dir = ""; + const std::string init_wfc = ""; + const double onsite_radius = 0.0; + const bool deepks_setorb = true; + const bool rpa = false; + const int nspin = 1; + const bool fixed_atoms = true; + const bool noncolin = false; + const std::string calculation = "md"; + const std::string esolver_type = "ksdft"; + EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, + basis_type, orbital_dir, init_wfc, + onsite_radius, deepks_setorb, rpa)); + EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); + EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); + EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); + EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, GlobalV::ofs_warning, nspin, + basis_type, orbital_dir, init_wfc, + onsite_radius, fixed_atoms, noncolin, + calculation, esolver_type, 0)); + ofs_running.close(); + GlobalV::ofs_warning.close(); + ifa.close(); + // check warning file + std::ifstream ifs_tmp; + ifs_tmp.open("read_atom_positions.warn"); + std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); + EXPECT_THAT(str, testing::HasSubstr("read_atoms warning : no atoms can move in MD simulations!")); + ifs_tmp.close(); + remove("read_atom_positions.tmp"); + remove("read_atom_positions.warn"); +} + +#endif // __LCAO diff --git a/source/source_cell/test/unitcell_test.cpp b/source/source_cell/test/unitcell_test.cpp index f78cf0239fc..630390c5b7d 100644 --- a/source/source_cell/test/unitcell_test.cpp +++ b/source/source_cell/test/unitcell_test.cpp @@ -3,11 +3,9 @@ #include "source_cell/read_stru.h" #include "memory" -#include "source_cell/read_stru.h" #include "source_base/global_variable.h" #include "source_base/mathzone.h" #include "prepare_unitcell.h" -#include "source_cell/read_stru.h" #include #include #include @@ -45,37 +43,6 @@ Magnetism::~Magnetism() * - check_dtau(): move all atomic coordinates into the first unitcell, i.e. in between [0,1) * - CheckTau * - check_tau(): check if any "two atoms are too close" - * - ReadAtomPositionsS1 - * - read_atom_positions(): spin 1 case - * - ReadAtomPositionsS2 - * - read_atom_positions(): spin 2 case - * - ReadAtomPositionsS4Noncolin - * - read_atom_positions(): spin 4 noncolinear case - * - ReadAtomPositionsS4Colin - * - read_atom_positions(): spin 4 colinear case - * - ReadAtomPositionsC - * - read_atom_positions(): Cartesian coordinates - * - ReadAtomPositionsCA - * - read_atom_positions(): Cartesian_angstrom coordinates - * - ReadAtomPositionsCACXY - * - read_atom_positions(): Cartesian_angstrom_center_xy coordinates - * - ReadAtomPositionsCACXZ - * - read_atom_positions(): Cartesian_angstrom_center_xz coordinates - * - ReadAtomPositionsCACXYZ - * - read_atom_positions(): Cartesian_angstrom_center_xyz coordinates - * - ReadAtomPositionsCAU - * - read_atom_positions(): Cartesian_au coordinates - * - ReadAtomPositionsWarning1 - * - read_atom_positions(): unknown type of coordinates - * - ReadAtomPositionsWarning2 - * - read_atom_positions(): atomic label inconsistency between ATOM_POSITIONS - * and ATOM_SPECIES - * - ReadAtomPositionsWarning3 - * - read_atom_positions(): warning : atom number < 0 - * - ReadAtomPositionsWarning4 - * - read_atom_positions(): mismatch in atom number for atom type - * - ReadAtomPositionsWarning5 - * - read_atom_positions(): no atoms can move in MD simulations! */ class UcellTest : public ::testing::Test @@ -320,796 +287,3 @@ TEST_F(UcellTest, CheckTauTrue) EXPECT_EQ(unitcell::check_tau(ucell->atoms ,ucell->ntype, ucell->lat0),true); GlobalV::ofs_warning.close(); } - -#ifdef __LCAO -class UcellTestReadStru : public ::testing::Test -{ - protected: - std::unique_ptr ucell{new UnitCell}; - std::string output; - void SetUp() override - { - ucell->ntype = 2; - ucell->pseudo_fn.resize(ucell->ntype); - ucell->pseudo_type.resize(ucell->ntype); - ucell->orbital_fn.resize(ucell->ntype); - } - void TearDown() override - { - ucell->orbital_fn.shrink_to_fit(); - } -}; - -TEST_F(UcellTestReadStru, ReadAtomPositionsS1) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsS2) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 2; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsS4Noncolin) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 4; - const bool fixed_atoms = false; - const bool noncolin = true; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsS4Colin) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 4; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsC) -{ - std::string fn = "./support/STRU_MgO_c"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsCA) -{ - std::string fn = "./support/STRU_MgO_ca"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsCACXY) -{ - std::string fn = "./support/STRU_MgO_cacxy"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsCACXZ) -{ - std::string fn = "./support/STRU_MgO_cacxz"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsCACYZ) -{ - std::string fn = "./support/STRU_MgO_cacyz"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsCACXYZ) -{ - std::string fn = "./support/STRU_MgO_cacxyz"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsCAU) -{ - std::string fn = "./support/STRU_MgO_cau"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = true; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsAutosetMag) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - int nspin = 2; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - for (int it = 0; it < ucell->ntype; it++) - { - for (int ia = 0; ia < ucell->atoms[it].na; ia++) - { - EXPECT_DOUBLE_EQ(ucell->atoms[it].mag[ia], 1.0); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].z, 1.0); - } - } - // for nspin == 4 - // Issue #5939: nspin=4 with no mag in STRU no longer autosets (1,1,1); - // all moments stay zero and a warning is emitted instead. - nspin = 4; - testing::internal::CaptureStdout(); - unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0); - const std::string stdout_output = testing::internal::GetCapturedStdout(); - for (int it = 0; it < ucell->ntype; it++) - { - for (int ia = 0; ia < ucell->atoms[it].na; ia++) - { - EXPECT_DOUBLE_EQ(ucell->atoms[it].mag[ia], 0.0); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].x, 0.0); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].y, 0.0); - EXPECT_DOUBLE_EQ(ucell->atoms[it].m_loc_[ia].z, 0.0); - } - } - // The zero-moment warning must reach both stdout and the running log. - EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), - std::string::npos); - ofs_running.flush(); - std::ifstream ifs_log("read_atom_positions.tmp"); - std::string log_content; - std::string log_line; - while (std::getline(ifs_log, log_line)) - { - log_content += log_line; - } - ifs_log.close(); - EXPECT_NE(log_content.find("no initial magnetization is set in STRU"), - std::string::npos); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsWarning1) -{ - std::string fn = "./support/STRU_MgO_WarningC1"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0)); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - // check warning file - std::ifstream ifs_tmp; - ifs_tmp.open("read_atom_positions.warn"); - std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("There are several options for you:")); - EXPECT_THAT(str, testing::HasSubstr("Direct")); - EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom")); - EXPECT_THAT(str, testing::HasSubstr("Cartesian_au")); - EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_xy")); - EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_xz")); - EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_yz")); - EXPECT_THAT(str, testing::HasSubstr("Cartesian_angstrom_center_xyz")); - ifs_tmp.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsWarning2) -{ - std::string fn = "./support/STRU_MgO_WarningC2"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0)); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - // check warning file - std::ifstream ifs_tmp; - ifs_tmp.open("read_atom_positions.warn"); - std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("Label read from ATOMIC_POSITIONS is Mo")); - EXPECT_THAT(str, testing::HasSubstr("Label from ATOMIC_SPECIES is Mg")); - ifs_tmp.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsWarning3) -{ - std::string fn = "./support/STRU_MgO_WarningC3"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_positions.tmp"); - GlobalV::ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, GlobalV::ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0)); - ofs_running.close(); - GlobalV::ofs_warning.close(); - ifa.close(); - // check warning file - std::ifstream ifs_tmp; - ifs_tmp.open("read_atom_positions.warn"); - std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("read_atom_positions warning : atom number < 0.")); - ifs_tmp.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsWarning4) -{ - std::string fn = "./support/STRU_MgO_WarningC4"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - std::ofstream ofs_warning; - ofs_running.open("read_atom_positions.tmp"); - ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->orbital_fn.resize(ucell->ntype); - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = false; - const bool noncolin = false; - const std::string calculation = "scf"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - testing::internal::CaptureStdout(); - EXPECT_EXIT(unitcell::read_atom_positions(*ucell, ifa, ofs_running, ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0), ::testing::ExitedWithCode(1), ""); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output, testing::HasSubstr("read_atom_positions, mismatch in atom number for atom type: Mg")); - ofs_running.close(); - ofs_warning.close(); - ifa.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} - -TEST_F(UcellTestReadStru, ReadAtomPositionsWarning5) -{ - std::string fn = "./support/STRU_MgO"; - std::ifstream ifa(fn.c_str()); - std::ofstream ofs_running; - ofs_running.open("read_atom_positions.tmp"); - GlobalV::ofs_warning.open("read_atom_positions.warn"); - // mandatory preliminaries - ucell->ntype = 2; - ucell->atoms = new Atom[ucell->ntype]; - ucell->set_atom_flag = true; - const std::string basis_type = "lcao"; - const std::string orbital_dir = ""; - const std::string init_wfc = ""; - const double onsite_radius = 0.0; - const bool deepks_setorb = true; - const bool rpa = false; - const int nspin = 1; - const bool fixed_atoms = true; - const bool noncolin = false; - const std::string calculation = "md"; - const std::string esolver_type = "ksdft"; - EXPECT_NO_THROW(unitcell::read_atom_species(ifa, ofs_running, *ucell, - basis_type, orbital_dir, init_wfc, - onsite_radius, deepks_setorb, rpa)); - EXPECT_NO_THROW(unitcell::read_lattice_constant(ifa, ofs_running,ucell->lat)); - EXPECT_DOUBLE_EQ(ucell->latvec.e11, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e22, 4.27957); - EXPECT_DOUBLE_EQ(ucell->latvec.e33, 4.27957); - EXPECT_NO_THROW(unitcell::read_atom_positions(*ucell, ifa, ofs_running, GlobalV::ofs_warning, nspin, - basis_type, orbital_dir, init_wfc, - onsite_radius, fixed_atoms, noncolin, - calculation, esolver_type, 0)); - ofs_running.close(); - GlobalV::ofs_warning.close(); - ifa.close(); - // check warning file - std::ifstream ifs_tmp; - ifs_tmp.open("read_atom_positions.warn"); - std::string str((std::istreambuf_iterator(ifs_tmp)), std::istreambuf_iterator()); - EXPECT_THAT(str, testing::HasSubstr("read_atoms warning : no atoms can move in MD simulations!")); - ifs_tmp.close(); - remove("read_atom_positions.tmp"); - remove("read_atom_positions.warn"); -} -#endif From 8c314ef4b6e396a1f0750e992898e9b4fdb1e607 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 6 Oct 2026 19:38:17 +0800 Subject: [PATCH 12/21] update initialized mag --- tests/01_PW/035_PW_15_SO/STRU | 8 ++++---- tests/01_PW/057_PW_SO_IW/STRU | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/01_PW/035_PW_15_SO/STRU b/tests/01_PW/035_PW_15_SO/STRU index 901aa2771d4..71a2da3c729 100644 --- a/tests/01_PW/035_PW_15_SO/STRU +++ b/tests/01_PW/035_PW_15_SO/STRU @@ -15,9 +15,9 @@ Direct //Cartesian or Direct coordinate. As 0 1 -0.2500000 0.2500000 0.25000000 0 0 0 +0.2500000 0.2500000 0.25000000 0 0 0 mag 1.7320508 -Ga //Element Label +Ga //Element Label 0 -1 //number of atom -0.00000 0.00000 0.000000 0 0 0 +1 //number of atom +0.00000 0.00000 0.000000 0 0 0 mag 1.7320508 diff --git a/tests/01_PW/057_PW_SO_IW/STRU b/tests/01_PW/057_PW_SO_IW/STRU index 901aa2771d4..71a2da3c729 100644 --- a/tests/01_PW/057_PW_SO_IW/STRU +++ b/tests/01_PW/057_PW_SO_IW/STRU @@ -15,9 +15,9 @@ Direct //Cartesian or Direct coordinate. As 0 1 -0.2500000 0.2500000 0.25000000 0 0 0 +0.2500000 0.2500000 0.25000000 0 0 0 mag 1.7320508 -Ga //Element Label +Ga //Element Label 0 -1 //number of atom -0.00000 0.00000 0.000000 0 0 0 +1 //number of atom +0.00000 0.00000 0.000000 0 0 0 mag 1.7320508 From 7e79a8acbe270d7506f92c90d142abc07341987f Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 7 Oct 2026 07:24:54 +0800 Subject: [PATCH 13/21] move 057_PW_SO_IW test to scf_in_wf_spin4 --- tests/01_PW/CASES_CPU.txt | 2 +- tests/01_PW/CASES_GPU.txt | 2 +- tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/INPUT | 0 tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/KPT | 0 .../01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/README | 0 tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/STRU | 0 .../01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/eig.txt | 0 .../{057_PW_SO_IW => scf_in_wf_spin4}/result.ref | 0 .../{057_PW_SO_IW => scf_in_wf_spin4}/wfk1s4_pw.dat | Bin 9 files changed, 2 insertions(+), 2 deletions(-) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/INPUT (100%) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/KPT (100%) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/README (100%) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/STRU (100%) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/eig.txt (100%) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/result.ref (100%) rename tests/01_PW/{057_PW_SO_IW => scf_in_wf_spin4}/wfk1s4_pw.dat (100%) diff --git a/tests/01_PW/CASES_CPU.txt b/tests/01_PW/CASES_CPU.txt index 6f2f88dedda..7b1217bc732 100644 --- a/tests/01_PW/CASES_CPU.txt +++ b/tests/01_PW/CASES_CPU.txt @@ -46,7 +46,7 @@ scf_Al100_kspace3 051_PW_OBOD_MemSaver scf_out_band_dos_wf 056_PW_IW -057_PW_SO_IW +scf_in_wf_spin4 058_PW_RE_MB 059_PW_RE_MB_traj 060_PW_RE_MG diff --git a/tests/01_PW/CASES_GPU.txt b/tests/01_PW/CASES_GPU.txt index ae8dd3be56f..e1c8970f9b4 100644 --- a/tests/01_PW/CASES_GPU.txt +++ b/tests/01_PW/CASES_GPU.txt @@ -45,7 +45,7 @@ scf_Al100_kspace3 051_PW_OBOD_MemSaver scf_out_band_dos_wf 056_PW_IW -057_PW_SO_IW +scf_in_wf_spin4 058_PW_RE_MB 059_PW_RE_MB_traj 060_PW_RE_MG diff --git a/tests/01_PW/057_PW_SO_IW/INPUT b/tests/01_PW/scf_in_wf_spin4/INPUT similarity index 100% rename from tests/01_PW/057_PW_SO_IW/INPUT rename to tests/01_PW/scf_in_wf_spin4/INPUT diff --git a/tests/01_PW/057_PW_SO_IW/KPT b/tests/01_PW/scf_in_wf_spin4/KPT similarity index 100% rename from tests/01_PW/057_PW_SO_IW/KPT rename to tests/01_PW/scf_in_wf_spin4/KPT diff --git a/tests/01_PW/057_PW_SO_IW/README b/tests/01_PW/scf_in_wf_spin4/README similarity index 100% rename from tests/01_PW/057_PW_SO_IW/README rename to tests/01_PW/scf_in_wf_spin4/README diff --git a/tests/01_PW/057_PW_SO_IW/STRU b/tests/01_PW/scf_in_wf_spin4/STRU similarity index 100% rename from tests/01_PW/057_PW_SO_IW/STRU rename to tests/01_PW/scf_in_wf_spin4/STRU diff --git a/tests/01_PW/057_PW_SO_IW/eig.txt b/tests/01_PW/scf_in_wf_spin4/eig.txt similarity index 100% rename from tests/01_PW/057_PW_SO_IW/eig.txt rename to tests/01_PW/scf_in_wf_spin4/eig.txt diff --git a/tests/01_PW/057_PW_SO_IW/result.ref b/tests/01_PW/scf_in_wf_spin4/result.ref similarity index 100% rename from tests/01_PW/057_PW_SO_IW/result.ref rename to tests/01_PW/scf_in_wf_spin4/result.ref diff --git a/tests/01_PW/057_PW_SO_IW/wfk1s4_pw.dat b/tests/01_PW/scf_in_wf_spin4/wfk1s4_pw.dat similarity index 100% rename from tests/01_PW/057_PW_SO_IW/wfk1s4_pw.dat rename to tests/01_PW/scf_in_wf_spin4/wfk1s4_pw.dat From f42d24ec562f8c85a3cfa587c841c313ea162252 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 7 Oct 2026 07:27:34 +0800 Subject: [PATCH 14/21] move 035_PW_15_SO to scf_spin4 --- tests/01_PW/CASES_CPU.txt | 2 +- tests/01_PW/CASES_GPU.txt | 2 +- tests/01_PW/{035_PW_15_SO => scf_spin4}/INPUT | 0 tests/01_PW/{035_PW_15_SO => scf_spin4}/KPT | 0 tests/01_PW/{035_PW_15_SO => scf_spin4}/README | 0 tests/01_PW/{035_PW_15_SO => scf_spin4}/STRU | 0 tests/01_PW/{035_PW_15_SO => scf_spin4}/result.ref | 0 7 files changed, 2 insertions(+), 2 deletions(-) rename tests/01_PW/{035_PW_15_SO => scf_spin4}/INPUT (100%) rename tests/01_PW/{035_PW_15_SO => scf_spin4}/KPT (100%) rename tests/01_PW/{035_PW_15_SO => scf_spin4}/README (100%) rename tests/01_PW/{035_PW_15_SO => scf_spin4}/STRU (100%) rename tests/01_PW/{035_PW_15_SO => scf_spin4}/result.ref (100%) diff --git a/tests/01_PW/CASES_CPU.txt b/tests/01_PW/CASES_CPU.txt index 7b1217bc732..e145798111b 100644 --- a/tests/01_PW/CASES_CPU.txt +++ b/tests/01_PW/CASES_CPU.txt @@ -32,7 +32,7 @@ scf_out_chg_tau 029_PW_15_CF_CS_S1_smallg 030_PW_15_CF_CS_S2_smallg 032_PW_15_CF_CS_bspline -035_PW_15_SO +scf_spin4 036_PW_AF 038_PW_NC 038_PW_NC_GGA_GRAD2 diff --git a/tests/01_PW/CASES_GPU.txt b/tests/01_PW/CASES_GPU.txt index e1c8970f9b4..f4dc0654527 100644 --- a/tests/01_PW/CASES_GPU.txt +++ b/tests/01_PW/CASES_GPU.txt @@ -32,7 +32,7 @@ scf_out_elf 029_PW_15_CF_CS_S1_smallg 030_PW_15_CF_CS_S2_smallg 032_PW_15_CF_CS_bspline -035_PW_15_SO +scf_spin4 036_PW_AF #038_PW_NC scf_Al100_kspace3 diff --git a/tests/01_PW/035_PW_15_SO/INPUT b/tests/01_PW/scf_spin4/INPUT similarity index 100% rename from tests/01_PW/035_PW_15_SO/INPUT rename to tests/01_PW/scf_spin4/INPUT diff --git a/tests/01_PW/035_PW_15_SO/KPT b/tests/01_PW/scf_spin4/KPT similarity index 100% rename from tests/01_PW/035_PW_15_SO/KPT rename to tests/01_PW/scf_spin4/KPT diff --git a/tests/01_PW/035_PW_15_SO/README b/tests/01_PW/scf_spin4/README similarity index 100% rename from tests/01_PW/035_PW_15_SO/README rename to tests/01_PW/scf_spin4/README diff --git a/tests/01_PW/035_PW_15_SO/STRU b/tests/01_PW/scf_spin4/STRU similarity index 100% rename from tests/01_PW/035_PW_15_SO/STRU rename to tests/01_PW/scf_spin4/STRU diff --git a/tests/01_PW/035_PW_15_SO/result.ref b/tests/01_PW/scf_spin4/result.ref similarity index 100% rename from tests/01_PW/035_PW_15_SO/result.ref rename to tests/01_PW/scf_spin4/result.ref From b8ce15c9430b3266ced49e839b94703f97144fb3 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 7 Oct 2026 07:40:22 +0800 Subject: [PATCH 15/21] add comments about mag for nspin=4 calculations --- source/source_cell/cal_ux.cpp | 13 +++++++++++++ source/source_cell/read_atoms_helper.cpp | 7 +++++++ source/source_hamilt/module_xc/xc_grad_prepare.cpp | 7 +++++++ source/source_hamilt/module_xc/xc_grad_utils.cpp | 10 ++++++++++ 4 files changed, 37 insertions(+) diff --git a/source/source_cell/cal_ux.cpp b/source/source_cell/cal_ux.cpp index 80201310323..38638c31afc 100644 --- a/source/source_cell/cal_ux.cpp +++ b/source/source_cell/cal_ux.cpp @@ -2,6 +2,19 @@ namespace unitcell { +// Compute the global spin quantization axis ux_ for non-collinear (nspin=4) +// calculations. ux_ is a single axis shared by the whole system (NOT one axis +// per atom); it is consumed by XC_Functional::noncolin_rho to split the spin +// density matrix into spin-up/down scalars for the LSDA/GGA kernel. +// +// The axis is derived from the *initial* moments m_loc_ read from STRU +// (direction of the first atom with |m| > threshold), not from the converged +// density. Consequently the STRU initial moments silently select the XC +// projection axis: with all-zero initial moments lsign_ stays false and the +// XC projection becomes direction-blind (neg = +1 everywhere in +// noncolin_rho), which can follow a different SCF path than a run started +// with an explicit non-zero moment even when the starting density (e.g. from +// init_chg = wfc/file) is identical. void cal_ux(UnitCell& ucell, const int nspin) { if (nspin != 4) diff --git a/source/source_cell/read_atoms_helper.cpp b/source/source_cell/read_atoms_helper.cpp index c440d7ef8af..3f1e13adeb1 100644 --- a/source/source_cell/read_atoms_helper.cpp +++ b/source/source_cell/read_atoms_helper.cpp @@ -302,6 +302,13 @@ void process_magnetization(Atom& atom, int it, int ia, if(!noncolin) { // collinear case with nspin = 4, only z component is used + // Note: a scalar "mag " in STRU is stored purely in + // m_loc_.z (x/y stay zero), so it passes the check below without + // triggering the warning and still defines a non-zero z moment. + // cal_ux() then turns that into the global XC quantization axis + // ux_ = (0,0,1) with lsign_ = true. Any positive scalar suffices; + // only the *direction* enters ux_, the magnitude (e.g. 1.7320508 + // vs 1.0) is irrelevant. if(std::abs(atom.m_loc_[ia].x) > 1e-5 || std::abs(atom.m_loc_[ia].y) > 1e-5) { std::stringstream ss; diff --git a/source/source_hamilt/module_xc/xc_grad_prepare.cpp b/source/source_hamilt/module_xc/xc_grad_prepare.cpp index b60d0d45207..fe9187f18c9 100644 --- a/source/source_hamilt/module_xc/xc_grad_prepare.cpp +++ b/source/source_hamilt/module_xc/xc_grad_prepare.cpp @@ -152,6 +152,13 @@ void gradcorr_prepare_rho( } // Mode 1 ignores the global quantization axis to remain continuous // when the magnetic moments tilt away from a collinear state. + // use_global_axis selects the up/down projection inside noncolin_rho: + // - true (lsign_ set, i.e. STRU initial moments defined a non-zero + // axis via cal_ux): sign follows m(r).ux_ point by point; + // - false (lsign_ unset / Mode 1): neg = +1 everywhere, a + // direction-blind |m| projection. + // This is why two otherwise identical runs can follow different SCF + // paths depending solely on whether STRU carries an initial moment. const bool use_global_axis = ucell->magnet.lsign_ && params.gga_grad != 1; XC_Functional::noncolin_rho(buf.rhotmp1.data(), buf.rhotmp2.data(), buf.neg.data(), chr->rho, rhopw->nrxx, ucell->magnet.ux_, use_global_axis); rhopw->real2recip(buf.rhotmp1.data(), buf.rhogsum1.data()); diff --git a/source/source_hamilt/module_xc/xc_grad_utils.cpp b/source/source_hamilt/module_xc/xc_grad_utils.cpp index 2f14e600886..cfbc0fa3d9d 100644 --- a/source/source_hamilt/module_xc/xc_grad_utils.cpp +++ b/source/source_hamilt/module_xc/xc_grad_utils.cpp @@ -167,6 +167,16 @@ void XC_Functional::noncolin_rho( #ifdef _OPENMP #pragma omp parallel for #endif + // Note: with lsign_ = true the up/down channels are defined w.r.t. the + // global axis ux_ through sign(m . ux_), evaluated point by point above; + // with lsign_ = false, "up" is always the local |m| (neg stays +1 + // everywhere). amag = |m(r)| is non-negative; neg[ir] carries that sign. + // The global axis ux_ itself is derived from the STRU initial moments in + // cal_ux(). Because GGA also consumes the *gradients* of rhoout1/rhoout2, + // a flipping sign field changes the gradient terms too, so the two + // branches can give noticeably different v_xc even for predominantly + // single-sign mz. (Locked by GgaGradTools.NoncolinRhoGlobalAxis in + // module_xc/test/test_xc5.cpp.) for(int ir = 0;ir Date: Wed, 7 Oct 2026 07:40:39 +0800 Subject: [PATCH 16/21] try to fix bug for nspin=4 --- tests/03_NAO_multik/scf_out_dos_spin4/STRU | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/03_NAO_multik/scf_out_dos_spin4/STRU b/tests/03_NAO_multik/scf_out_dos_spin4/STRU index 79211e7719f..04e65cbc54f 100644 --- a/tests/03_NAO_multik/scf_out_dos_spin4/STRU +++ b/tests/03_NAO_multik/scf_out_dos_spin4/STRU @@ -19,9 +19,9 @@ Direct //Cartesian or Direct coordinate. As 0 1 -0.2600000 0.2400000 0.245000000 0 0 0 +0.2600000 0.2400000 0.245000000 0 0 0 m 1.7 Ga //Element Label 0 1 //number of atom -0.00000 0.00000 0.000000 0 0 0 +0.00000 0.00000 0.000000 0 0 0 m 1.7 From d8f6322b0afbdf79d5da7a5253f0797f6775124c Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 8 Oct 2026 17:21:21 +0800 Subject: [PATCH 17/21] update mag --- tests/03_NAO_multik/scf_out_dos_spin4/STRU | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/03_NAO_multik/scf_out_dos_spin4/STRU b/tests/03_NAO_multik/scf_out_dos_spin4/STRU index 04e65cbc54f..3e2f64acfb1 100644 --- a/tests/03_NAO_multik/scf_out_dos_spin4/STRU +++ b/tests/03_NAO_multik/scf_out_dos_spin4/STRU @@ -19,9 +19,9 @@ Direct //Cartesian or Direct coordinate. As 0 1 -0.2600000 0.2400000 0.245000000 0 0 0 m 1.7 +0.2600000 0.2400000 0.245000000 0 0 0 mag 1.7 Ga //Element Label 0 1 //number of atom -0.00000 0.00000 0.000000 0 0 0 m 1.7 +0.00000 0.00000 0.000000 0 0 0 mag 1.7 From 45afdb8f39f3329e1b37d40a0afd39658f04c894 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 9 Oct 2026 12:49:36 +0800 Subject: [PATCH 18/21] fix bug by change slightly the reference forces of the example 03_NAO_multik/scf_out_dos_spin4 --- tests/03_NAO_multik/scf_out_dos_spin4/INPUT | 4 ++-- tests/03_NAO_multik/scf_out_dos_spin4/STRU | 8 ++++---- tests/03_NAO_multik/scf_out_dos_spin4/result.ref | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/03_NAO_multik/scf_out_dos_spin4/INPUT b/tests/03_NAO_multik/scf_out_dos_spin4/INPUT index b57f2e84774..12ceec9e763 100644 --- a/tests/03_NAO_multik/scf_out_dos_spin4/INPUT +++ b/tests/03_NAO_multik/scf_out_dos_spin4/INPUT @@ -12,7 +12,7 @@ smearing_method gaussian smearing_sigma 0.001 #Parameters (3.PW) ecutwfc 10 -scf_thr 1e-6 +scf_thr 1e-8 scf_nmax 100 #noncolin 1 @@ -24,7 +24,7 @@ cal_stress 1 #Parameters (LCAO) basis_type lcao ks_solver scalapack_gvx -pw_diag_thr 0.00001 +pw_diag_thr 1e-5 mixing_type pulay mixing_beta 0.7 diff --git a/tests/03_NAO_multik/scf_out_dos_spin4/STRU b/tests/03_NAO_multik/scf_out_dos_spin4/STRU index 3e2f64acfb1..d5e6bfc9d72 100644 --- a/tests/03_NAO_multik/scf_out_dos_spin4/STRU +++ b/tests/03_NAO_multik/scf_out_dos_spin4/STRU @@ -19,9 +19,9 @@ Direct //Cartesian or Direct coordinate. As 0 1 -0.2600000 0.2400000 0.245000000 0 0 0 mag 1.7 +0.2600000 0.2400000 0.245000000 0 0 0 mag 1.7320508 -Ga //Element Label +Ga //Element Label 0 -1 //number of atom -0.00000 0.00000 0.000000 0 0 0 mag 1.7 +1 //number of atom +0.00000 0.00000 0.000000 0 0 0 mag 1.7320508 diff --git a/tests/03_NAO_multik/scf_out_dos_spin4/result.ref b/tests/03_NAO_multik/scf_out_dos_spin4/result.ref index e54b51d3064..8f9915deded 100644 --- a/tests/03_NAO_multik/scf_out_dos_spin4/result.ref +++ b/tests/03_NAO_multik/scf_out_dos_spin4/result.ref @@ -1,6 +1,6 @@ -etotref -1964.0663947982770878 -etotperatomref -982.0331973991 -totalforceref 0.162158 -totalstressref 1877.059089 -totaldosref 38 -totaltimeref 16.23 +etotref -1964.0663947983236994 +etotperatomref -982.0331973992 +totalforceref 0.162676 +totalstressref 1877.059085 +totaldosref 38.000000 +totaltimeref 5.26 From 355d2a3ce511657ccb7d6fd05d97117a83d57144 Mon Sep 17 00:00:00 2001 From: Critsium Date: Fri, 9 Oct 2026 18:53:55 +0800 Subject: [PATCH 19/21] fix(cell): report the nspin=4 zero-moment start on every code path The zero-moment warning added for #5939 was unreachable in the two cases that matter most: * autoset_magnetization() keyed the check on atom.mag, which for a three-component "magmom" holds the full norm including x/y. With nspin=4 and noncolin=0 the x/y components are projected away, leaving m_loc_ = (0,0,0) while mag stays non-zero, so the warning was skipped for exactly the silent zero-moment start it was meant to surface. * read_atom_positions() only calls autoset_magnetization() when symmetry != 1, and symmetry defaults to 1 for nspin=4 + noncolin=1 + lspinorb=0 scf runs with k-points, so the documented warning never appeared there either. Split the magnetization reporting out of autoset_magnetization(): * is_magnetization_all_zero() shares the all-zero test. * warn_zero_magnetization_nspin4() keys on m_loc_, the vector cal_ux() actually consumes, and read_atom_positions() calls it for every symmetry setting. * autoset_magnetization() now only handles the nspin=2 autoset. Also stop printing the x/y-discarded warning once per atom. The block right below it in process_magnetization() throttles its output for the same reason; a 2000-atom cell with tilted moments produced ~4000 lines of identical text before SCF started. process_magnetization() now returns whether the projection discarded anything and read_atom_positions() aggregates it per atom type, reporting the affected-atom count once. Both warnings now also go through ModuleBase::WARNING so they reach warning.log, and the multi-line text is written straight to ofs_running instead of through GlobalFunc::OUT, whose setw(40) key/value layout was broken by the embedded newlines. Finally, the "initial magmom is all zero and symmetry=1" message is no longer printed for an explicitly magnetized STRU. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DNd7fpeHXHuL9DKME85J44 --- source/source_cell/read_atoms.cpp | 41 +++- source/source_cell/read_atoms_helper.cpp | 131 +++++++---- source/source_cell/read_atoms_helper.h | 37 ++- .../source_cell/test/read_atoms_hlp_test.cpp | 214 ++++++++++++------ 4 files changed, 311 insertions(+), 112 deletions(-) diff --git a/source/source_cell/read_atoms.cpp b/source/source_cell/read_atoms.cpp index d3991d662eb..f5223a7a162 100644 --- a/source/source_cell/read_atoms.cpp +++ b/source/source_cell/read_atoms.cpp @@ -70,6 +70,11 @@ bool unitcell::read_atom_positions(UnitCell& ucell, int na = ucell.atoms[it].na; ucell.nat += na; + // Number of atoms of this type whose x/y moment was discarded by + // the collinear projection (nspin=4 with noncolin=0). Reported + // once per type after the atom loop instead of once per atom. + int n_xy_discarded = 0; + if (na > 0) { unitcell::allocate_atom_properties(ucell.atoms[it], na); @@ -103,9 +108,14 @@ bool unitcell::read_atom_positions(UnitCell& ucell, } // Process magnetization - unitcell::process_magnetization(ucell.atoms[it], it, ia, nspin, - input_vec_mag, input_angle_mag, ofs_running, - noncolin); + const bool xy_discarded + = unitcell::process_magnetization(ucell.atoms[it], it, ia, nspin, + input_vec_mag, input_angle_mag, ofs_running, + noncolin); + if (xy_discarded) + { + ++n_xy_discarded; + } // Transform coordinates unitcell::transform_atom_coordinates(ucell.atoms[it], ia, Coordinate, @@ -117,6 +127,9 @@ bool unitcell::read_atom_positions(UnitCell& ucell, ucell.atoms[it].dis[ia].set(0, 0, 0); }//endj } // end na + + unitcell::warn_xy_magnetization_ignored(ucell.atoms[it].label, + n_xy_discarded, na, ofs_running); // reset some useless parameters if (set_element_mag_zero) { @@ -130,15 +143,29 @@ bool unitcell::read_atom_positions(UnitCell& ucell, // so do not override it with an autoset seed. Warn instead. if (symmetry == 1) { - ofs_running << "\n WARNING: initial magmom is all zero and symmetry=1; " - << "autoset magnetism is SKIPPED to preserve the symmetry of the initial (nonmagnetic) structure.\n" - << " If spontaneous magnetism is expected, set magmom explicitly " - << "in STRU, or use symmetry = 0 or -1." << std::endl; + // Only report the skipped autoset when there is in fact + // nothing set: this message used to be printed even for an + // explicitly magnetized STRU. + if (unitcell::is_magnetization_all_zero(ucell)) + { + ofs_running << "\n WARNING: initial magmom is all zero and symmetry=1; " + << "autoset magnetism is SKIPPED to preserve the symmetry of the initial (nonmagnetic) structure.\n" + << " If spontaneous magnetism is expected, set magmom explicitly " + << "in STRU, or use symmetry = 0 or -1." << std::endl; + } } else { unitcell::autoset_magnetization(ucell, nspin, ofs_running); } + + // nspin=4 never autosets a moment, so the zero-moment start has to + // be reported for every symmetry setting, not only on the autoset + // path that symmetry=1 skips. + if (nspin == 4) + { + unitcell::warn_zero_magnetization_nspin4(ucell, ofs_running); + } } // end scan_begin // Final validation and output diff --git a/source/source_cell/read_atoms_helper.cpp b/source/source_cell/read_atoms_helper.cpp index 0d998a88402..58ce696db68 100644 --- a/source/source_cell/read_atoms_helper.cpp +++ b/source/source_cell/read_atoms_helper.cpp @@ -17,6 +17,9 @@ namespace { constexpr char LOWER_A = 'a'; constexpr char LOWER_Z = 'z'; constexpr char MINUS_SIGN = '-'; + + // Below this value a magnetic moment component is treated as zero. + constexpr double mag_threshold = 1e-5; } namespace unitcell { @@ -79,51 +82,97 @@ void set_atom_movement_flags(Atom& atom, int ia, } } -void autoset_magnetization(UnitCell& ucell, int nspin, - std::ofstream& ofs_running) +bool is_magnetization_all_zero(const UnitCell& ucell) { - const int ntype = ucell.ntype; - - // Check if any atom has non-zero magnetization - int autoset_mag = 1; - for (int it = 0; it < ntype; it++) + for (int it = 0; it < ucell.ntype; it++) { for (int ia = 0; ia < ucell.atoms[it].na; ia++) { - if(std::abs(ucell.atoms[it].mag[ia]) > 1e-5) + if (std::abs(ucell.atoms[it].mag[ia]) > mag_threshold) { - autoset_mag = 0; - break; + return false; } } } + return true; +} - if (autoset_mag) +void autoset_magnetization(UnitCell& ucell, int nspin, + std::ofstream& ofs_running) +{ + // nspin=4 never autosets a moment. An all-zero moment in STRU is a + // legitimate nonmagnetic start; warn_zero_magnetization_nspin4() reports + // it instead, independently of the symmetry switch that gates this call. + if (nspin != 2) + { + return; + } + + if (!is_magnetization_all_zero(ucell)) { - if(nspin==4) + return; + } + + const int ntype = ucell.ntype; + for (int it = 0; it < ntype; it++) + { + for (int ia = 0; ia < ucell.atoms[it].na; ia++) { - // nspin=4 with all-zero magnetization: do NOT autoset, - // start from zero and let the user decide explicitly. - std::string msg = "nspin=4 but no initial magnetization is set in STRU.\n" - " All atoms start from zero magnetic moment.\n" - " If a magnetic ground state is expected, set 'mag' explicitly " - "in STRU for the magnetic atoms."; - std::cout << " Warning: " << msg << std::endl; - ModuleBase::GlobalFunc::OUT(ofs_running, "Warning", msg); + ucell.atoms[it].mag[ia] = 1.0; + ucell.atoms[it].m_loc_[ia].z = ucell.atoms[it].mag[ia]; + ModuleBase::GlobalFunc::OUT(ofs_running,"Autoset magnetism for this atom", 1.0); } - else if(nspin==2) + } +} + +void warn_zero_magnetization_nspin4(const UnitCell& ucell, + std::ofstream& ofs_running) +{ + const double threshold_sq = mag_threshold * mag_threshold; + for (int it = 0; it < ucell.ntype; it++) + { + for (int ia = 0; ia < ucell.atoms[it].na; ia++) { - for (int it = 0; it < ntype; it++) + const ModuleBase::Vector3& m = ucell.atoms[it].m_loc_[ia]; + const double norm_sq = m.x * m.x + m.y * m.y + m.z * m.z; + if (norm_sq > threshold_sq) { - for (int ia = 0; ia < ucell.atoms[it].na; ia++) - { - ucell.atoms[it].mag[ia] = 1.0; - ucell.atoms[it].m_loc_[ia].z = ucell.atoms[it].mag[ia]; - ModuleBase::GlobalFunc::OUT(ofs_running,"Autoset magnetism for this atom", 1.0); - } + return; } } } + + const std::string summary = "nspin=4 but no initial magnetization is set in STRU; " + "all atoms start from zero magnetic moment."; + std::cout << " Warning: " << summary << std::endl; + ModuleBase::WARNING("read_atom_positions", summary); + ofs_running << "\n WARNING: " << summary << std::endl; + ofs_running << " If a magnetic ground state is expected, set 'mag' explicitly " + << "in STRU for the magnetic atoms." << std::endl; + ofs_running << " With noncolin=0 the x/y components of 'mag' are discarded, so a " + << "moment given purely along x/y also leaves a zero starting moment." + << std::endl; +} + +void warn_xy_magnetization_ignored(const std::string& label, + int n_discarded, + int na, + std::ofstream& ofs_running) +{ + if (n_discarded <= 0) + { + return; + } + + std::stringstream ss; + ss << "atom type " << label << ": " << n_discarded << " of " << na + << " atoms give non-zero x/y magnetization in STRU, but nspin=4 with noncolin=0 " + << "is a collinear calculation; only the z component is used. " + << "x/y components are IGNORED. Set 'noncolin 1' to use the full vector."; + const std::string summary = ss.str(); + std::cout << " Warning: " << summary << std::endl; + ModuleBase::WARNING("read_atom_positions", summary); + ofs_running << "\n WARNING: " << summary << std::endl; } bool finalize_atom_positions(UnitCell& ucell, @@ -260,12 +309,14 @@ void transform_atom_coordinates(Atom& atom, int ia, } } -void process_magnetization(Atom& atom, int it, int ia, +bool process_magnetization(Atom& atom, int it, int ia, int nspin, bool input_vec_mag, bool input_angle_mag, std::ofstream& ofs_running, const bool noncolin) { + bool xy_discarded = false; + // Recalculate mag and m_loc_ from read in angle1, angle2 and mag or mx, my, mz if(input_angle_mag) { @@ -304,22 +355,20 @@ void process_magnetization(Atom& atom, int it, int ia, // collinear case with nspin = 4, only z component is used // Note: a scalar "mag " in STRU is stored purely in // m_loc_.z (x/y stay zero), so it passes the check below without - // triggering the warning and still defines a non-zero z moment. + // flagging a discard and still defines a non-zero z moment. // cal_ux() then turns that into the global XC quantization axis // ux_ = (0,0,1) with lsign_ = true. Any positive scalar suffices; // only the *direction* enters ux_, the magnitude (e.g. 1.7320508 // vs 1.0) is irrelevant. - if(std::abs(atom.m_loc_[ia].x) > 1e-5 || std::abs(atom.m_loc_[ia].y) > 1e-5) + // + // The discard is only reported back to the caller, which + // aggregates it per atom type: emitting a message here would + // print one warning per atom for a cell where every atom tilts + // the moment. + if(std::abs(atom.m_loc_[ia].x) > mag_threshold + || std::abs(atom.m_loc_[ia].y) > mag_threshold) { - std::stringstream ss; - ss << "Atom " << ia+1 << " of type " << atom.label - << ": STRU gives non-zero x/y magnetization (" - << atom.m_loc_[ia].x << ", " << atom.m_loc_[ia].y << ", " - << atom.m_loc_[ia].z << "), but nspin=4 with noncolin=0 " - << "is a collinear calculation; only the z component is used.\n" - << " x/y components are IGNORED. Set 'noncolin 1' to use the full vector."; - std::cout << " Warning: " << ss.str() << std::endl; - ModuleBase::GlobalFunc::OUT(ofs_running, "Warning", ss.str()); + xy_discarded = true; } atom.m_loc_[ia].x = 0; atom.m_loc_[ia].y = 0; @@ -361,6 +410,8 @@ void process_magnetization(Atom& atom, int it, int ia, ModuleBase::GlobalFunc::OUT(ofs_running, ss.str(),atom.mag[ia]); } } + + return xy_discarded; } bool parse_atom_properties(std::ifstream& ifpos, diff --git a/source/source_cell/read_atoms_helper.h b/source/source_cell/read_atoms_helper.h index 05d6091cd81..7cb39af3e30 100644 --- a/source/source_cell/read_atoms_helper.h +++ b/source/source_cell/read_atoms_helper.h @@ -45,6 +45,39 @@ void set_atom_movement_flags(Atom& atom, int ia, void autoset_magnetization(UnitCell& ucell, int nspin, std::ofstream& ofs_running); +/** + * @brief Check whether every atom carries a zero collinear magnetic moment + * @param ucell Unit cell object + * @return true if |mag| of every atom is below the magnetization threshold + */ +bool is_magnetization_all_zero(const UnitCell& ucell); + +/** + * @brief Warn when a nspin=4 run starts from an all-zero magnetization vector + * + * The check uses m_loc_, the vector actually consumed by cal_ux(), and not + * mag: the collinear projection applied when noncolin=0 can leave mag + * non-zero while m_loc_ is already zero, so keying the check on mag would + * miss exactly the silent zero-moment start this warning reports. + * + * @param ucell Unit cell object + * @param ofs_running Output stream for running information + */ +void warn_zero_magnetization_nspin4(const UnitCell& ucell, + std::ofstream& ofs_running); + +/** + * @brief Report, once per atom type, that x/y magnetization was discarded + * @param label Element label of the atom type + * @param n_discarded Number of atoms of this type whose x/y moment was dropped + * @param na Number of atoms of this type + * @param ofs_running Output stream for running information + */ +void warn_xy_magnetization_ignored(const std::string& label, + int n_discarded, + int na, + std::ofstream& ofs_running); + /** * @brief Perform final validation and output * @param ucell Unit cell object @@ -94,8 +127,10 @@ void transform_atom_coordinates(Atom& atom, int ia, * @param input_vec_mag Whether vector magnetization was input * @param input_angle_mag Whether angle magnetization was input * @param ofs_running Output stream for running information + * @return true if the x/y components were discarded by the collinear + * projection applied when nspin=4 and noncolin=0 */ -void process_magnetization(Atom& atom, int it, int ia, +bool process_magnetization(Atom& atom, int it, int ia, int nspin, bool input_vec_mag, bool input_angle_mag, std::ofstream& ofs_running, diff --git a/source/source_cell/test/read_atoms_hlp_test.cpp b/source/source_cell/test/read_atoms_hlp_test.cpp index 2b9c28e113a..c536fa4eabf 100644 --- a/source/source_cell/test/read_atoms_hlp_test.cpp +++ b/source/source_cell/test/read_atoms_hlp_test.cpp @@ -769,18 +769,11 @@ TEST_F(ReadAtomsHelperTest, RoundTripMultiAtomMixedFields) delete[] ucell.atoms; } -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} - // ============================================================ -// Tests for issue #5021: nspin=4 with noncolin=0 should warn -// when x/y components are non-zero +// Tests for issue #5021: nspin=4 with noncolin=0 discards x/y // ============================================================ -TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearWarnsOnXY) +TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearReportsXYDiscard) { Atom atom; atom.label = "Fe"; @@ -797,32 +790,20 @@ TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearWarnsOnXY) const bool input_angle_mag = false; const bool noncolin = false; - // Capture stdout + // The discard is reported to the caller, not printed once per atom. testing::internal::CaptureStdout(); - unitcell::process_magnetization(atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); - std::string stdout_output = testing::internal::GetCapturedStdout(); + const bool xy_discarded = unitcell::process_magnetization( + atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // x/y should be zeroed + EXPECT_TRUE(xy_discarded); EXPECT_DOUBLE_EQ(atom.m_loc_[0].x, 0.0); EXPECT_DOUBLE_EQ(atom.m_loc_[0].y, 0.0); EXPECT_DOUBLE_EQ(atom.m_loc_[0].z, 0.3); - - // Warning should appear on stdout - EXPECT_NE(stdout_output.find("Warning"), std::string::npos); - EXPECT_NE(stdout_output.find("x/y components are IGNORED"), std::string::npos); - - // Warning should appear in running log - ofs_running.flush(); - std::ifstream ifs("test_running.log"); - std::stringstream buffer; - buffer << ifs.rdbuf(); - std::string log_content = buffer.str(); - ifs.close(); - EXPECT_NE(log_content.find("Warning"), std::string::npos); - EXPECT_NE(log_content.find("x/y components are IGNORED"), std::string::npos); + EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); } -TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearNoWarnOnZeroXY) +TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearNoDiscardOnZeroXY) { Atom atom; atom.label = "Fe"; @@ -839,19 +820,16 @@ TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4CollinearNoWarnOnZeroXY) const bool input_angle_mag = false; const bool noncolin = false; - testing::internal::CaptureStdout(); - unitcell::process_magnetization(atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); - std::string stdout_output = testing::internal::GetCapturedStdout(); + const bool xy_discarded = unitcell::process_magnetization( + atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); + EXPECT_FALSE(xy_discarded); EXPECT_DOUBLE_EQ(atom.m_loc_[0].x, 0.0); EXPECT_DOUBLE_EQ(atom.m_loc_[0].y, 0.0); EXPECT_DOUBLE_EQ(atom.m_loc_[0].z, 0.3); - - // No warning should appear - EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); } -TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4NoncolinNoWarn) +TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4NoncolinKeepsXY) { Atom atom; atom.label = "Fe"; @@ -868,24 +846,69 @@ TEST_F(ReadAtomsHelperTest, ProcessMagnetizationNspin4NoncolinNoWarn) const bool input_angle_mag = false; const bool noncolin = true; - testing::internal::CaptureStdout(); - unitcell::process_magnetization(atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); - std::string stdout_output = testing::internal::GetCapturedStdout(); + const bool xy_discarded = unitcell::process_magnetization( + atom, 0, 0, nspin, input_vec_mag, input_angle_mag, ofs_running, noncolin); - // All components should be preserved + EXPECT_FALSE(xy_discarded); EXPECT_DOUBLE_EQ(atom.m_loc_[0].x, 1.0); EXPECT_DOUBLE_EQ(atom.m_loc_[0].y, 0.5); EXPECT_DOUBLE_EQ(atom.m_loc_[0].z, 0.3); +} + +TEST_F(ReadAtomsHelperTest, WarnXYMagnetizationIgnoredAggregatesPerType) +{ + testing::internal::CaptureStdout(); + unitcell::warn_xy_magnetization_ignored("Fe", 3, 4, ofs_running); + const std::string stdout_output = testing::internal::GetCapturedStdout(); + + // A single message for the whole type, carrying the affected-atom count. + const std::string::size_type first = stdout_output.find("Warning"); + EXPECT_NE(first, std::string::npos); + EXPECT_NE(stdout_output.find("atom type Fe: 3 of 4 atoms"), std::string::npos); + EXPECT_NE(stdout_output.find("x/y components are IGNORED"), std::string::npos); + EXPECT_EQ(stdout_output.find("Warning", first + 1), std::string::npos); + + ofs_running.flush(); + std::ifstream ifs("test_running.log"); + std::stringstream buffer; + buffer << ifs.rdbuf(); + const std::string log_content = buffer.str(); + ifs.close(); + EXPECT_NE(log_content.find("x/y components are IGNORED"), std::string::npos); +} + +TEST_F(ReadAtomsHelperTest, WarnXYMagnetizationIgnoredSilentWhenNoneDiscarded) +{ + testing::internal::CaptureStdout(); + unitcell::warn_xy_magnetization_ignored("Fe", 0, 4, ofs_running); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // No warning should appear EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); } // ============================================================ -// Tests for issue #5939: nspin=4 autoset should not force (1,1,1) +// Tests for issue #5939: nspin=4 no longer autosets (1,1,1) // ============================================================ -TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin4NoAutoset) +TEST_F(ReadAtomsHelperTest, IsMagnetizationAllZero) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 2; + ucell.atoms[0].mag.resize(2, 0.0); + ucell.atoms[0].m_loc_.resize(2); + + EXPECT_TRUE(unitcell::is_magnetization_all_zero(ucell)); + + ucell.atoms[0].mag[1] = 1.5; + EXPECT_FALSE(unitcell::is_magnetization_all_zero(ucell)); + + delete[] ucell.atoms; +} + +TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin4NoAutosetAndSilent) { UnitCell ucell; ucell.ntype = 1; @@ -900,53 +923,93 @@ TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin4NoAutoset) testing::internal::CaptureStdout(); unitcell::autoset_magnetization(ucell, nspin, ofs_running); - std::string stdout_output = testing::internal::GetCapturedStdout(); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // Magnetization should remain zero (NOT autoset to (1,1,1)) + // Magnetization stays zero (NOT autoset to (1,1,1)), and this function + // stays silent: reporting belongs to warn_zero_magnetization_nspin4(), + // which read_atom_positions() calls for every symmetry setting. EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].x, 0.0); EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].y, 0.0); EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 0.0); EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 0.0); + EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); + + delete[] ucell.atoms; +} + +TEST_F(ReadAtomsHelperTest, WarnZeroMagnetizationNspin4) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 1; + ucell.atoms[0].mag.resize(1, 0.0); + ucell.atoms[0].m_loc_.resize(1); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 0.0); + + testing::internal::CaptureStdout(); + unitcell::warn_zero_magnetization_nspin4(ucell, ofs_running); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // Warning should appear on stdout EXPECT_NE(stdout_output.find("Warning"), std::string::npos); - EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), std::string::npos); + EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), + std::string::npos); - // Warning should appear in running log ofs_running.flush(); std::ifstream ifs("test_running.log"); std::stringstream buffer; buffer << ifs.rdbuf(); - std::string log_content = buffer.str(); + const std::string log_content = buffer.str(); ifs.close(); - EXPECT_NE(log_content.find("Warning"), std::string::npos); - EXPECT_NE(log_content.find("no initial magnetization is set in STRU"), std::string::npos); + EXPECT_NE(log_content.find("no initial magnetization is set in STRU"), + std::string::npos); delete[] ucell.atoms; } -TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin4NoWarnWhenMagSet) +TEST_F(ReadAtomsHelperTest, WarnZeroMagnetizationNspin4KeysOnMLoc) { + // Regression guard: a three-component magmom lying purely in x/y leaves + // mag at the full norm (2.0) while m_loc_ has already been projected to + // zero by the noncolin=0 branch. Keying this check on mag would silence + // the warning in exactly that case. UnitCell ucell; ucell.ntype = 1; ucell.atoms = new Atom[1]; ucell.atoms[0].label = "Fe"; ucell.atoms[0].na = 1; - ucell.atoms[0].mag.resize(1, 1.5); + ucell.atoms[0].mag.resize(1, 2.0); ucell.atoms[0].m_loc_.resize(1); - ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 1.5); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 0.0); - const int nspin = 4; + EXPECT_FALSE(unitcell::is_magnetization_all_zero(ucell)); testing::internal::CaptureStdout(); - unitcell::autoset_magnetization(ucell, nspin, ofs_running); - std::string stdout_output = testing::internal::GetCapturedStdout(); + unitcell::warn_zero_magnetization_nspin4(ucell, ofs_running); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // No autoset should happen - EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 1.5); - EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 1.5); + EXPECT_NE(stdout_output.find("no initial magnetization is set in STRU"), + std::string::npos); + + delete[] ucell.atoms; +} + +TEST_F(ReadAtomsHelperTest, WarnZeroMagnetizationNspin4SilentWhenMagSet) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 1; + ucell.atoms[0].mag.resize(1, 1.5); + ucell.atoms[0].m_loc_.resize(1); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 1.5); + + testing::internal::CaptureStdout(); + unitcell::warn_zero_magnetization_nspin4(ucell, ofs_running); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // No warning should appear EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); delete[] ucell.atoms; @@ -967,14 +1030,37 @@ TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin2StillAutoset) testing::internal::CaptureStdout(); unitcell::autoset_magnetization(ucell, nspin, ofs_running); - std::string stdout_output = testing::internal::GetCapturedStdout(); + const std::string stdout_output = testing::internal::GetCapturedStdout(); - // nspin=2 should still autoset to 1.0 (VASP-compatible behavior) + // nspin=2 still autosets to 1.0 (VASP-compatible behavior) EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 1.0); EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 1.0); - - // No warning for nspin=2 autoset EXPECT_EQ(stdout_output.find("Warning"), std::string::npos); delete[] ucell.atoms; } + +TEST_F(ReadAtomsHelperTest, AutosetMagnetizationNspin2SkippedWhenMagSet) +{ + UnitCell ucell; + ucell.ntype = 1; + ucell.atoms = new Atom[1]; + ucell.atoms[0].label = "Fe"; + ucell.atoms[0].na = 1; + ucell.atoms[0].mag.resize(1, 1.5); + ucell.atoms[0].m_loc_.resize(1); + ucell.atoms[0].m_loc_[0].set(0.0, 0.0, 1.5); + + unitcell::autoset_magnetization(ucell, 2, ofs_running); + + EXPECT_DOUBLE_EQ(ucell.atoms[0].mag[0], 1.5); + EXPECT_DOUBLE_EQ(ucell.atoms[0].m_loc_[0].z, 1.5); + + delete[] ucell.atoms; +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From c6c648057dcb3ed0933af0ced4f31cca226d07a3 Mon Sep 17 00:00:00 2001 From: Critsium Date: Fri, 9 Oct 2026 18:54:27 +0800 Subject: [PATCH 20/21] docs: align the magnetization autoset docs with the new behavior docs/advanced/input_files/stru.md still told users that nspin=4 autosets "mag 1 1 1" on every atom, which this branch removes; that page is where the STRU "mag" field is looked up, so it contradicted the notes added to input-main.md. Rewrite the section to describe the zero-moment start, the explicit-mag example, the x/y discard under noncolin=0, and the symmetry=1 case in which the nspin=2 autoset is skipped as well. Also record the autoset rules under "nspin" itself rather than only under "noncolin": the behavior is a property of nspin. parameters.yaml and input-main.md are regenerated from the C++ Input_Item description so the documentation-consistency check stays satisfied. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DNd7fpeHXHuL9DKME85J44 --- docs/advanced/input_files/input-main.md | 2 ++ docs/advanced/input_files/stru.md | 25 +++++++++++++------ docs/parameters.yaml | 2 ++ .../module_parameter/read_inp_estruc.cpp | 4 ++- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/advanced/input_files/input-main.md b/docs/advanced/input_files/input-main.md index bacad14219c..1c03c939811 100644 --- a/docs/advanced/input_files/input-main.md +++ b/docs/advanced/input_files/input-main.md @@ -1363,6 +1363,8 @@ - 1: Spin degeneracy - 2: Collinear spin polarized. - 4: Noncollinear or spin-orbit calculations. Set nspin to 4 explicitly when noncolin or lspinorb is enabled. + - Note: With nspin=2 and no initial magnetization in STRU, a moment of 1.0 is autoset for every atom, unless symmetry is 1. + - Note: With nspin=4 no moment is ever autoset. The calculation starts from zero magnetic moment and a warning is printed; set 'mag' explicitly in STRU for the magnetic atoms if a magnetic ground state is expected. - **Default**: 1 ### gga_grad diff --git a/docs/advanced/input_files/stru.md b/docs/advanced/input_files/stru.md index a50cfb6022e..885a1e833fc 100644 --- a/docs/advanced/input_files/stru.md +++ b/docs/advanced/input_files/stru.md @@ -281,7 +281,7 @@ For general usage requirements, the APNSv1.0 pseudopotential and orbital set is 0.0 0.0 0.0 m 0 0 0 0.5 0.5 0.5 m 1 1 1 ``` - For `nspin==2`, we will autoset atomic magmon is `1.0`: + For `nspin==2`, the atomic magnetic moment is autoset to `1.0`: ``` Fe 1.0 @@ -295,21 +295,32 @@ For general usage requirements, the APNSv1.0 pseudopotential and orbital set is 0.0 0.0 0.0 m 0 0 0 0.5 0.5 0.5 m 1 1 1 ``` - For `nspin==4`, we will autoset atomic magmon as follow: + This autoset is skipped as soon as `STRU` specifies a finite moment for + any single atom, and also when [`symmetry`](input-main.md#symmetry) is + `1`, because an all-zero moment is a legitimate nonmagnetic choice under + the full point group. + + For `nspin==4`, **no** moment is autoset: the calculation starts from a + zero magnetic moment and a warning is printed. Zero magnetization is a + stationary point, so such a run stays nonmagnetic. Set `mag` explicitly + on the magnetic atoms if a magnetic ground state is expected, e.g.: ``` Fe 0.0 2 - 0.0 0.0 0.0 m 0 0 0 mag 1 1 1 - 0.5 0.5 0.5 m 1 1 1 mag 1 1 1 + 0.0 0.0 0.0 m 0 0 0 mag 0.0 0.0 2.0 + 0.5 0.5 0.5 m 1 1 1 mag 0.0 0.0 2.0 O 0.0 2 - 0.0 0.0 0.0 m 0 0 0 mag 1 1 1 - 0.5 0.5 0.5 m 1 1 1 mag 1 1 1 + 0.0 0.0 0.0 m 0 0 0 + 0.5 0.5 0.5 m 1 1 1 ``` - However, this autoset will not be vaild once `STRU` specalize a finite magnetic for any single atom. + With [`noncolin`](input-main.md#noncolin) `0`, only the z component of + `mag` is used; the x/y components are discarded and the number of + affected atoms is reported per atom type. A moment given purely along + x/y therefore also leaves a zero starting moment. - `lambda`: Lagrange multiplier vector for spin constraint method. Can specify one value (z-component) or three values for x, y, z components (e.g., `lambda 0.5` or `lambda 0.1 0.2 0.3`). Values are in eV and will be converted to Rydberg internally. Used with spin-constrained DFT (enable with `sc_mag_switch` in INPUT file). diff --git a/docs/parameters.yaml b/docs/parameters.yaml index 02b5e0d5e91..a0e9742fe1e 100644 --- a/docs/parameters.yaml +++ b/docs/parameters.yaml @@ -709,6 +709,8 @@ parameters: * 1: Spin degeneracy * 2: Collinear spin polarized. * 4: Noncollinear or spin-orbit calculations. Set nspin to 4 explicitly when noncolin or lspinorb is enabled. + * Note: With nspin=2 and no initial magnetization in STRU, a moment of 1.0 is autoset for every atom, unless symmetry is 1. + * Note: With nspin=4 no moment is ever autoset. The calculation starts from zero magnetic moment and a warning is printed; set 'mag' explicitly in STRU for the magnetic atoms if a magnetic ground state is expected. default_value: "1" unit: "" availability: "" diff --git a/source/source_io/module_parameter/read_inp_estruc.cpp b/source/source_io/module_parameter/read_inp_estruc.cpp index 11247ebea7f..5da535085ca 100644 --- a/source/source_io/module_parameter/read_inp_estruc.cpp +++ b/source/source_io/module_parameter/read_inp_estruc.cpp @@ -472,7 +472,9 @@ The other way is only available when compiling with LIBXC, and it allows for sup item.description = R"(The number of spin components of wave functions. * 1: Spin degeneracy * 2: Collinear spin polarized. -* 4: Noncollinear or spin-orbit calculations. Set nspin to 4 explicitly when noncolin or lspinorb is enabled.)"; +* 4: Noncollinear or spin-orbit calculations. Set nspin to 4 explicitly when noncolin or lspinorb is enabled. +* Note: With nspin=2 and no initial magnetization in STRU, a moment of 1.0 is autoset for every atom, unless symmetry is 1. +* Note: With nspin=4 no moment is ever autoset. The calculation starts from zero magnetic moment and a warning is printed; set 'mag' explicitly in STRU for the magnetic atoms if a magnetic ground state is expected.)"; item.default_value = "1"; item.unit = ""; read_sync_int(input.nspin); From ee5d253d98017f3d3f28ffba61c44bbf8cb58deb Mon Sep 17 00:00:00 2001 From: Critsium Date: Fri, 9 Oct 2026 18:54:27 +0800 Subject: [PATCH 21/21] chore: follow the test rename and separate a shared test log * .ci/slurm/config.ini still registered case.080 as 01_PW/035_PW_15_SO, a directory this branch renamed to scf_spin4, so that GPU case looked for a path that no longer exists. (19 further entries in that file already point at missing directories from earlier renames; those are left alone.) * PrintCellTest.PrintCell and UcellTest.PrintUnitcellPseudo both used "printcell.log". Before the unitcell_test.cpp split they shared one translation unit and ran sequentially; they are now separate executables that AddTest runs in the same working directory, so under ctest -j each could read or delete the other's file. Give the print_cell test its own name. * Keep the #pragma omp parallel for adjacent to the loop it governs in noncolin_rho(): the explanatory comment sat between the two, which compiles but invites an edit that drops a statement into the gap. * Strip the trailing blanks added to the regenerated 03_NAO_multik/scf_out_dos_spin4 INPUT and STRU. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01DNd7fpeHXHuL9DKME85J44 --- .ci/slurm/config.ini | 2 +- source/source_cell/test/test_print_cell.cpp | 6 +++--- source/source_hamilt/module_xc/xc_grad_utils.cpp | 6 +++--- tests/03_NAO_multik/scf_out_dos_spin4/INPUT | 4 ++-- tests/03_NAO_multik/scf_out_dos_spin4/STRU | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.ci/slurm/config.ini b/.ci/slurm/config.ini index 3aeb77cba5f..89432701553 100644 --- a/.ci/slurm/config.ini +++ b/.ci/slurm/config.ini @@ -491,7 +491,7 @@ runner = autotest_gpu [case.080] suite = 01_PW -name = 035_PW_15_SO +name = scf_spin4 resource = pw_gpu1 runner = autotest_gpu diff --git a/source/source_cell/test/test_print_cell.cpp b/source/source_cell/test/test_print_cell.cpp index d411ca576a8..b1d131ffcb4 100644 --- a/source/source_cell/test/test_print_cell.cpp +++ b/source/source_cell/test/test_print_cell.cpp @@ -331,18 +331,18 @@ TEST_F(PrintCellTest, PrintCell) UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; ucell = utp.SetUcellInfo(); std::ofstream ofs; - ofs.open("printcell.log"); + ofs.open("print_cell_test.log"); unitcell::print_cell(*ucell, ofs); ofs.close(); std::ifstream ifs; - ifs.open("printcell.log"); + ifs.open("print_cell_test.log"); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); EXPECT_THAT(str, testing::HasSubstr("latName = bcc")); EXPECT_THAT(str, testing::HasSubstr("ntype = 2")); EXPECT_THAT(str, testing::HasSubstr("nat = 3")); EXPECT_THAT(str, testing::HasSubstr("GGT :")); EXPECT_THAT(str, testing::HasSubstr("omega = 6748.33")); - remove("printcell.log"); + remove("print_cell_test.log"); } TEST_F(PrintCellTest, PrintTauDirect) { diff --git a/source/source_hamilt/module_xc/xc_grad_utils.cpp b/source/source_hamilt/module_xc/xc_grad_utils.cpp index cfbc0fa3d9d..6fe1dac62ca 100644 --- a/source/source_hamilt/module_xc/xc_grad_utils.cpp +++ b/source/source_hamilt/module_xc/xc_grad_utils.cpp @@ -164,9 +164,6 @@ void XC_Functional::noncolin_rho( } } } -#ifdef _OPENMP -#pragma omp parallel for -#endif // Note: with lsign_ = true the up/down channels are defined w.r.t. the // global axis ux_ through sign(m . ux_), evaluated point by point above; // with lsign_ = false, "up" is always the local |m| (neg stays +1 @@ -177,6 +174,9 @@ void XC_Functional::noncolin_rho( // branches can give noticeably different v_xc even for predominantly // single-sign mz. (Locked by GgaGradTools.NoncolinRhoGlobalAxis in // module_xc/test/test_xc5.cpp.) +#ifdef _OPENMP +#pragma omp parallel for +#endif for(int ir = 0;ir