88#include " source_basis/module_pw/test/test_tool.h"
99#include " mpi.h"
1010#endif
11+ #include < fstream>
12+ #include < sstream>
1113
1214/* *
1315 * - Tested Functions:
1416 * - read_rhog()
17+ * - write_rhog()
1518 */
1619
1720class ReadRhogTest : public ::testing::Test
@@ -21,27 +24,60 @@ class ReadRhogTest : public ::testing::Test
2124 std::vector<std::vector<std::complex <double >>> rhog_data;
2225 std::vector<std::complex <double >*> rhog;
2326 Parallel::ParaWorld pw_world = Parallel::make_pw_world();
27+ std::ofstream warning_stream;
28+
29+ void setup_pw_basis ()
30+ {
31+ #ifdef __MPI
32+ rhopw.initmpi (pw_world.size (), pw_world.rank (), pw_world.comm ());
33+ #endif
34+ rhopw.initgrids (6.5 , ModuleBase::Matrix3 (-0.5 , 0.0 , 0.5 , 0.0 , 0.5 , 0.5 , -0.5 , 0.5 , 0.0 ), 120 );
35+ rhopw.initparameters (false , 120 );
36+ rhopw.setuptransform ();
37+ rhopw.collect_local_pw ();
38+ }
39+
40+ void open_warning (const std::string& path)
41+ {
42+ warning_stream.open (path);
43+ }
44+
45+ void close_warning ()
46+ {
47+ if (warning_stream.is_open ())
48+ {
49+ warning_stream.close ();
50+ }
51+ }
52+
53+ std::string read_warning_file (const std::string& path)
54+ {
55+ std::ifstream ifs (path);
56+ std::stringstream ss;
57+ ss << ifs.rdbuf ();
58+ ifs.close ();
59+ return ss.str ();
60+ }
2461
2562 virtual void SetUp ()
2663 {
2764 rhog_data.resize (1 , std::vector<std::complex <double >>(1471 ));
2865 rhog.push_back (rhog_data[0 ].data ());
2966 }
67+
68+ virtual void TearDown ()
69+ {
70+ close_warning ();
71+ }
3072};
3173
32- // Test the read_rhog function
74+ // Test the read_rhog function with normal file
3375TEST_F (ReadRhogTest, ReadRhog)
3476{
3577 std::string filename = " ./support/charge-density.dat" ;
36- #ifdef __MPI
37- rhopw.initmpi (pw_world.size (), pw_world.rank (), pw_world.comm ());
38- #endif
39- rhopw.initgrids (6.5 , ModuleBase::Matrix3 (-0.5 , 0.0 , 0.5 , 0.0 , 0.5 , 0.5 , -0.5 , 0.5 , 0.0 ), 120 );
40- rhopw.initparameters (false , 120 );
41- rhopw.setuptransform ();
42- rhopw.collect_local_pw ();
78+ setup_pw_basis ();
4379
44- bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, &GlobalV::ofs_warning)
80+ bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, nullptr );
4581
4682 EXPECT_TRUE (result);
4783 EXPECT_DOUBLE_EQ (rhog[0 ][0 ].real (), -1.0304462993299456e-05 );
@@ -57,46 +93,33 @@ TEST_F(ReadRhogTest, NotFoundFile)
5793{
5894 std::string filename = " notfound.txt" ;
5995
60- GlobalV::ofs_warning.open (" test_read_rhog.txt" );
61- bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, &GlobalV::ofs_warning)
62- GlobalV::ofs_warning.close ();
63-
64- std::ifstream ifs_running (" test_read_rhog.txt" );
65- std::stringstream ss;
66- ss << ifs_running.rdbuf ();
67- std::string file_content = ss.str ();
68- ifs_running.close ();
96+ open_warning (" test_read_rhog.txt" );
97+ bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, &warning_stream);
98+ close_warning ();
6999
70100 std::string expected_content = " ModuleIO::read_rhog warning : Can't open file notfound.txt\n " ;
71-
72101 EXPECT_FALSE (result);
73- EXPECT_EQ (file_content , expected_content);
102+ EXPECT_EQ (read_warning_file ( " test_read_rhog.txt " ) , expected_content);
74103 std::remove (" test_read_rhog.txt" );
75104}
76105
77- // Test the read_rhog function when tgamma_only is inconsistent
106+ // Test the read_rhog function when gamma_only is inconsistent
78107TEST_F (ReadRhogTest, InconsistentGammaOnly)
79108{
80109 std::string filename = " ./support/charge-density.dat" ;
81110 rhopw.gamma_only = true ;
82111
83- GlobalV::ofs_warning.open (" test_read_rhog.txt" );
84- bool result = ModuleIO::read_rhog (filename, &rhopw, 2 , rhog.data (), pw_world, &GlobalV::ofs_warning);
85- GlobalV::ofs_warning.close ();
86-
87- std::ifstream ifs_running (" test_read_rhog.txt" );
88- std::stringstream ss;
89- ss << ifs_running.rdbuf ();
90- std::string file_content = ss.str ();
91- ifs_running.close ();
112+ open_warning (" test_read_rhog.txt" );
113+ bool result = ModuleIO::read_rhog (filename, &rhopw, 2 , rhog.data (), pw_world, &warning_stream);
114+ close_warning ();
92115
93116 std::string expected_content
94117 = " ModuleIO::read_rhog warning : some planewaves in file are not used\n ModuleIO::read_rhog warning : some "
95118 " spin channels in file are missing\n ModuleIO::read_rhog warning : gamma_only read from file is "
96119 " inconsistent with INPUT\n " ;
97120
98121 EXPECT_FALSE (result);
99- EXPECT_EQ (file_content , expected_content);
122+ EXPECT_EQ (read_warning_file ( " test_read_rhog.txt " ) , expected_content);
100123 std::remove (" test_read_rhog.txt" );
101124}
102125
@@ -106,23 +129,131 @@ TEST_F(ReadRhogTest, SomePWMissing)
106129 std::string filename = " ./support/charge-density.dat" ;
107130 rhopw.npwtot = 2000 ;
108131
109- GlobalV::ofs_warning.open (" test_read_rhog.txt" );
110- bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, &GlobalV::ofs_warning)
111- GlobalV::ofs_warning.close ();
112-
113- std::ifstream ifs_running (" test_read_rhog.txt" );
114- std::stringstream ss;
115- ss << ifs_running.rdbuf ();
116- std::string file_content = ss.str ();
117- ifs_running.close ();
132+ open_warning (" test_read_rhog.txt" );
133+ bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, &warning_stream);
134+ close_warning ();
118135
119136 std::string expected_content = " ModuleIO::read_rhog warning : some planewaves in file are missing\n " ;
120-
121137 EXPECT_TRUE (result);
122- EXPECT_EQ (file_content , expected_content);
138+ EXPECT_EQ (read_warning_file ( " test_read_rhog.txt " ) , expected_content);
123139 std::remove (" test_read_rhog.txt" );
124140}
125141
142+ // Test read_rhog with os_warning=nullptr (silent mode, must not crash)
143+ TEST_F (ReadRhogTest, OsNullptrSilent)
144+ {
145+ std::string filename = " notfound.txt" ;
146+ bool result = ModuleIO::read_rhog (filename, &rhopw, 1 , rhog.data (), pw_world, nullptr );
147+ EXPECT_FALSE (result);
148+ }
149+
150+ // Test write_rhog round-trip: write then read back, verify data consistency
151+ TEST_F (ReadRhogTest, WriteRoundTrip)
152+ {
153+ setup_pw_basis ();
154+
155+ // initialize some rhog data
156+ rhog_data[0 ].assign (rhopw.npw , std::complex <double >(1.5 , 2.5 ));
157+
158+ std::string tmpfile = " test_rhog_roundtrip.dat" ;
159+
160+ // write
161+ bool write_result = ModuleIO::write_rhog (
162+ tmpfile, rhopw.gamma_only , &rhopw, 1 ,
163+ ModuleBase::Matrix3 (-0.5 , 0.0 , 0.5 , 0.0 , 0.5 , 0.5 , -0.5 , 0.5 , 0.0 ),
164+ rhog.data (), pw_world, nullptr );
165+ EXPECT_TRUE (write_result);
166+
167+ // read back into a fresh buffer
168+ std::vector<std::vector<std::complex <double >>> rhog_read_data (
169+ 1 , std::vector<std::complex <double >>(rhopw.npw ));
170+ std::vector<std::complex <double >*> rhog_read;
171+ rhog_read.push_back (rhog_read_data[0 ].data ());
172+
173+ bool read_result = ModuleIO::read_rhog (tmpfile, &rhopw, 1 , rhog_read.data (), pw_world, nullptr );
174+ EXPECT_TRUE (read_result);
175+
176+ // compare: within MPI precision tolerance
177+ int diff_count = 0 ;
178+ for (int ig = 0 ; ig < rhopw.npw ; ++ig)
179+ {
180+ if (std::abs (rhog[0 ][ig] - rhog_read[0 ][ig]) > 1e-10 )
181+ {
182+ ++diff_count;
183+ }
184+ }
185+ EXPECT_EQ (diff_count, 0 ) << diff_count << " planewave values differ after round-trip" ;
186+
187+ std::remove (tmpfile.c_str ());
188+ }
189+
190+ // Test write_rhog when the output path is not writable
191+ TEST_F (ReadRhogTest, WriteFileFail)
192+ {
193+ setup_pw_basis ();
194+ rhog_data[0 ].assign (rhopw.npw , std::complex <double >(1.0 , 0.0 ));
195+
196+ // try to write to a directory path (not a file) — should fail
197+ bool result = ModuleIO::write_rhog (
198+ " /tmp" , rhopw.gamma_only , &rhopw, 1 ,
199+ ModuleBase::Matrix3 (-0.5 , 0.0 , 0.5 , 0.0 , 0.5 , 0.5 , -0.5 , 0.5 , 0.0 ),
200+ rhog.data (), pw_world, nullptr );
201+ EXPECT_FALSE (result);
202+ }
203+
204+ // Test write_rhog with nspin=2, round-trip both channels
205+ TEST_F (ReadRhogTest, WriteRoundTripNspin2)
206+ {
207+ setup_pw_basis ();
208+
209+ // expand to nspin=2
210+ rhog_data.resize (2 , std::vector<std::complex <double >>(rhopw.npw ));
211+ rhog.clear ();
212+ rhog.push_back (rhog_data[0 ].data ());
213+ rhog.push_back (rhog_data[1 ].data ());
214+
215+ // initialize distinct values for each spin channel
216+ for (int ig = 0 ; ig < rhopw.npw ; ++ig)
217+ {
218+ rhog_data[0 ][ig] = std::complex <double >(1.0 * ig, 0.1 * ig);
219+ rhog_data[1 ][ig] = std::complex <double >(2.0 * ig, 0.2 * ig);
220+ }
221+
222+ std::string tmpfile = " test_rhog_roundtrip_nspin2.dat" ;
223+
224+ // write nspin=2
225+ bool write_result = ModuleIO::write_rhog (
226+ tmpfile, rhopw.gamma_only , &rhopw, 2 ,
227+ ModuleBase::Matrix3 (-0.5 , 0.0 , 0.5 , 0.0 , 0.5 , 0.5 , -0.5 , 0.5 , 0.0 ),
228+ rhog.data (), pw_world, nullptr );
229+ EXPECT_TRUE (write_result);
230+
231+ // read back
232+ std::vector<std::vector<std::complex <double >>> rhog_read_data (
233+ 2 , std::vector<std::complex <double >>(rhopw.npw ));
234+ std::vector<std::complex <double >*> rhog_read;
235+ rhog_read.push_back (rhog_read_data[0 ].data ());
236+ rhog_read.push_back (rhog_read_data[1 ].data ());
237+
238+ bool read_result = ModuleIO::read_rhog (tmpfile, &rhopw, 2 , rhog_read.data (), pw_world, nullptr );
239+ EXPECT_TRUE (read_result);
240+
241+ int diff_count = 0 ;
242+ for (int is = 0 ; is < 2 ; ++is)
243+ {
244+ for (int ig = 0 ; ig < rhopw.npw ; ++ig)
245+ {
246+ if (std::abs (rhog[is][ig] - rhog_read[is][ig]) > 1e-10 )
247+ {
248+ ++diff_count;
249+ }
250+ }
251+ }
252+ EXPECT_EQ (diff_count, 0 ) << diff_count << " planewave values differ after nspin=2 round-trip" ;
253+
254+ std::remove (tmpfile.c_str ());
255+ }
256+
126257int main (int argc, char ** argv)
127258{
128259#ifdef __MPI
0 commit comments