From ae461ddcd69b583a9caf8935d1adcea9ba94e05a Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 14:47:40 +0800 Subject: [PATCH 01/30] refactor(out_app_flag): change out_app_flag from bool to int and add unified output directories - Change out_app_flag from bool to int with three states: 0: no append, no subdirectory (overwrite) 1: append to same file (default, backward compatible) 2: write to unified subdirectories - Add new global directory variables in System_para: global_psi_dir, global_chg_dir, global_pot_dir, global_elf_dir, global_dos_dir, global_dip_dir, global_mat_dir - Initialize directories in set_global_dir(): out_app_flag==2 -> OUT.suffix/{PSI,CHG,POT,ELF,DOS,DIP,MAT}/ otherwise -> all point to global_out_dir (backward compatible) - Map legacy WFC->PSI, matrix->MAT - Update all out_app_flag usages from bool to int semantics - Broadcast new directory variables via Parallel_Common::bcast_string Issue: #8151 --- source/source_base/module_out/filename.cpp | 10 +++--- source/source_base/module_out/filename.h | 4 +-- .../source_io/module_ctrl/ctrl_output_fp.cpp | 2 +- .../source_io/module_ctrl/ctrl_scf_lcao.cpp | 12 +++---- source/source_io/module_dhs/write_dh.cpp | 6 ++-- source/source_io/module_dos/cal_dos.cpp | 4 +-- source/source_io/module_dos/cal_dos.h | 2 +- .../source_io/module_dos/write_dos_lcao.cpp | 6 ++-- source/source_io/module_dos/write_dos_lcao.h | 2 +- source/source_io/module_dos/write_dos_pw.cpp | 2 +- source/source_io/module_dos/write_dos_pw.h | 2 +- .../source_io/module_energy/write_bands.cpp | 4 +-- .../source_io/module_hs/dhs_sparse_writer.cpp | 6 ++-- .../source_io/module_hs/dhs_sparse_writer.h | 2 +- source/source_io/module_hs/hs_sparse_io.cpp | 2 +- source/source_io/module_hs/hs_sparse_io.h | 2 +- source/source_io/module_hs/hsk_writer.cpp | 6 ++-- source/source_io/module_hs/hsk_writer.h | 2 +- source/source_io/module_hs/hsr_legacy.cpp | 8 ++--- source/source_io/module_hs/hsr_legacy.h | 4 +-- source/source_io/module_hs/hterm_writer.cpp | 10 +++--- source/source_io/module_hs/hterm_writer.h | 2 +- .../source_io/module_hs/output_mat_sparse.cpp | 2 +- source/source_io/module_hs/pos_op_mat.cpp | 2 +- source/source_io/module_hs/pos_op_mat.h | 2 +- source/source_io/module_hs/pos_op_writer.cpp | 6 ++-- source/source_io/module_hs/pos_op_writer.h | 2 +- source/source_io/module_hs/vxc_op_lip.cpp | 2 +- source/source_io/module_hs/vxc_op_mat.cpp | 2 +- .../module_parameter/input_parameter.h | 5 +-- .../module_parameter/read_inp_out.cpp | 18 +++++++--- .../module_parameter/read_set_globalv.cpp | 36 +++++++++++++++++++ .../module_parameter/system_parameter.h | 10 +++++- source/source_io/module_wf/read_wf2rho_pw.cpp | 2 +- source/source_io/module_wf/read_wfc_nao.cpp | 2 +- source/source_io/module_wf/write_wfc_nao.cpp | 10 +++--- source/source_io/module_wf/write_wfc_nao.h | 2 +- source/source_io/module_wf/write_wfc_pw.cpp | 2 +- source/source_psi/psi_init_file.cpp | 2 +- 39 files changed, 131 insertions(+), 76 deletions(-) diff --git a/source/source_base/module_out/filename.cpp b/source/source_base/module_out/filename.cpp index 4dd86dc2bea..c7fa58fb9eb 100644 --- a/source/source_base/module_out/filename.cpp +++ b/source/source_base/module_out/filename.cpp @@ -14,7 +14,7 @@ std::string filename_output( const int nspin, const int nkstot, const int out_type, - const bool out_app_flag, + const int out_app_flag, const bool gamma_only, const int istep, const int iter) @@ -97,14 +97,14 @@ std::string filename_output( } std::string istep_block - = (istep >= 0 && (!out_app_flag)) + = (istep >= 0 && (out_app_flag != 1)) ? "g" + std::to_string(istep + 1) // istep starts from 0 - : ""; // only when istep >= 0 and out_app_flag is false will write each wfc to a separate file + : ""; // only when istep >= 0 and out_app_flag is not append mode will write each wfc to a separate file std::string iter_block - = (iter >= 0 && (!out_app_flag)) + = (iter >= 0 && (out_app_flag != 1)) ? "e" + std::to_string(iter) // iter starts from 1 - : ""; // only when istep >= 0 and out_app_flag is false will write each wfc to a separate file + : ""; // only when istep >= 0 and out_app_flag is not append mode will write each wfc to a separate file std::string suffix_block; if (out_type == 1) diff --git a/source/source_base/module_out/filename.h b/source/source_base/module_out/filename.h index d76970c769f..d4c351e3a1e 100644 --- a/source/source_base/module_out/filename.h +++ b/source/source_base/module_out/filename.h @@ -16,7 +16,7 @@ namespace ModuleIO * @param nspin: number of spin channels, 1,2 or 4 * @param nkstot: number of total k-points * @param out_type: two types of output file format, 1 for .txt and 2 for .dat (binary) - * @param out_app_flag: whether to append to existing file. + * @param out_app_flag: 0=no append, 1=append to same file, 2=write to subdirectory. * @param gamma_only: gamma_only algorithm or not. * @param istep: index of the ion step starting from 0. If < 0, the step number is not included in the file name. * @param iter: index of the electronic step starting from 0. If < 0, the step number is not included in the file name. @@ -31,7 +31,7 @@ std::string filename_output( const int nspin, const int nkstot, const int out_type, - const bool out_app_flag, + const int out_app_flag, const bool gamma_only, const int istep=-1, const int iter=-1); diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 05bc5e6f6af..c6c08cc9a39 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -32,7 +32,7 @@ void ctrl_output_fp(UnitCell& ucell, ModuleBase::TITLE("ModuleIO", "ctrl_output_fp"); ModuleBase::timer::start("ModuleIO", "ctrl_output_fp"); - const bool out_app_flag = inp.out_app_flag; + const int out_app_flag = inp.out_app_flag; const bool gamma_only = PARAM.globalv.gamma_only_local; const int nspin = inp.nspin; const std::string global_out_dir = PARAM.globalv.global_out_dir; diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index b7533f0325d..7bd751b1a88 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -136,7 +136,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, //***** - const bool out_app_flag = inp.out_app_flag; + const int out_app_flag = inp.out_app_flag; const bool gamma_only = PARAM.globalv.gamma_only_local; const int nspin = inp.nspin; const std::string global_out_dir = PARAM.globalv.global_out_dir; @@ -179,7 +179,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, { const int precision = inp.out_dmr[1]; - ModuleIO::write_dmr(dm->get_dmr_vec(), &ucell, precision, pv, out_app_flag, + ModuleIO::write_dmr(dm->get_dmr_vec(), &ucell, precision, pv, (out_app_flag == 1), ucell.get_iat2iwt(), ucell.nat, istep, ofs_running); } @@ -279,7 +279,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, const hamilt::HContainer* sr = p_hamilt->getSR(); ModuleIO::write_hsr(hr_vec, sr, &ucell, inp.out_hsr[0], precision, pv, - out_app_flag, gamma_only, ucell.get_iat2iwt(), ucell.nat, istep, + (out_app_flag == 1), gamma_only, ucell.get_iat2iwt(), ucell.nat, istep, PARAM.globalv.global_out_dir, pelec->eferm, ofs_running); } @@ -399,7 +399,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, dh_params.nspin = inp.nspin; dh_params.istep = istep; dh_params.gamma_only = gamma_only; - dh_params.append = out_app_flag; + dh_params.append = (out_app_flag == 1); dh_params.ofs_running = &ofs_running; if (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 2) { @@ -442,7 +442,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, h_params.nrxx = pw_rho->nrxx; h_params.nspin = nspin; h_params.istep = istep; - h_params.append = out_app_flag; + h_params.append = (out_app_flag == 1); h_params.iat2iwt = ucell.get_iat2iwt(); h_params.nat = ucell.nat; h_params.nlocal = PARAM.globalv.nlocal; @@ -527,7 +527,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, false, // bit inp.out_mat_tk[1], 1, // true for upper triangle matrix - inp.out_app_flag, + (inp.out_app_flag == 1), t_fn, pv, GlobalV::DRANK, diff --git a/source/source_io/module_dhs/write_dh.cpp b/source/source_io/module_dhs/write_dh.cpp index 9a61cf3dee9..e0d99fde3ee 100644 --- a/source/source_io/module_dhs/write_dh.cpp +++ b/source/source_io/module_dhs/write_dh.cpp @@ -44,9 +44,9 @@ void write_dh_perI(WriteDHParams& params, const int nks = params.kv->get_nks() / nspin_k; const int nlocal = PARAM.globalv.nlocal; const std::string global_out_dir = PARAM.globalv.global_out_dir; - const bool out_app_flag = PARAM.inp.out_app_flag; + const int out_app_flag = PARAM.inp.out_app_flag; const std::string r_dir - = (PARAM.inp.calculation == "md" && !out_app_flag) ? PARAM.globalv.global_matrix_dir : global_out_dir; + = (PARAM.inp.calculation == "md" && (out_app_flag != 1)) ? PARAM.globalv.global_matrix_dir : global_out_dir; #ifdef __MPI Parallel_Orbitals serialV; @@ -129,7 +129,7 @@ void write_dh_perI(WriteDHParams& params, false, 8, false, - out_app_flag, + (out_app_flag == 1), fk, pv, GlobalV::DRANK, diff --git a/source/source_io/module_dos/cal_dos.cpp b/source/source_io/module_dos/cal_dos.cpp index 7975c5e6ded..79f5dd7d65a 100644 --- a/source/source_io/module_dos/cal_dos.cpp +++ b/source/source_io/module_dos/cal_dos.cpp @@ -108,7 +108,7 @@ bool ModuleIO::cal_dos(const int& is, // index for spin const ModuleBase::matrix& ekb, // energy for each k point and each band const ModuleBase::matrix& wg, // weight of k-points and bands const int istep, // ionic step - const bool out_app_flag, + const int out_app_flag, const int bndpar) { ModuleBase::TITLE("ModuleIO", "cal_dos"); @@ -117,7 +117,7 @@ bool ModuleIO::cal_dos(const int& is, // index for spin if (GlobalV::MY_RANK == 0) { - if(out_app_flag==true) + if(out_app_flag == 1) { ofs_dos.open(fn.c_str(), std::ios::app); } diff --git a/source/source_io/module_dos/cal_dos.h b/source/source_io/module_dos/cal_dos.h index d133c4e9423..e8f305b685b 100644 --- a/source/source_io/module_dos/cal_dos.h +++ b/source/source_io/module_dos/cal_dos.h @@ -37,7 +37,7 @@ namespace ModuleIO const ModuleBase::matrix &ekb, //store energy for each k point and each band const ModuleBase::matrix &wg, //weight of (kpoint,bands)) const int istep_in, // ionic_step - const bool out_app_flag, // whether to append output + const int out_app_flag, // 0: no append, 1: append, 2: subdirectory const int bndpar); // number of band parallel groups } diff --git a/source/source_io/module_dos/write_dos_lcao.cpp b/source/source_io/module_dos/write_dos_lcao.cpp index 926d94bd4e5..20ef0799aae 100644 --- a/source/source_io/module_dos/write_dos_lcao.cpp +++ b/source/source_io/module_dos/write_dos_lcao.cpp @@ -20,7 +20,7 @@ void write_dos_lcao( const double& dos_edelta_ev, const double& dos_scale, const double& bcoeff, - const bool out_app_flag, + const int out_app_flag, const int istep, const int nspin, const int nlocal, @@ -125,7 +125,7 @@ template void write_dos_lcao( const double& dos_edelta_ev, const double& dos_scale, const double& bcoeff, - const bool out_app_flag, + const int out_app_flag, const int istep, const int nspin, const int nlocal, @@ -152,7 +152,7 @@ template void write_dos_lcao( const double& dos_edelta_ev, const double& dos_scale, const double& bcoeff, - const bool out_app_flag, + const int out_app_flag, const int istep, const int nspin, const int nlocal, diff --git a/source/source_io/module_dos/write_dos_lcao.h b/source/source_io/module_dos/write_dos_lcao.h index 0e9017149a4..25e8b6d14a6 100644 --- a/source/source_io/module_dos/write_dos_lcao.h +++ b/source/source_io/module_dos/write_dos_lcao.h @@ -28,7 +28,7 @@ namespace ModuleIO const double& dos_edelta_ev, // Delta energy const double& dos_scale, const double& bcoeff, - const bool out_app_flag, + const int out_app_flag, const int istep, const int nspin, const int nlocal, diff --git a/source/source_io/module_dos/write_dos_pw.cpp b/source/source_io/module_dos/write_dos_pw.cpp index 1ce707f59de..3564a78f275 100644 --- a/source/source_io/module_dos/write_dos_pw.cpp +++ b/source/source_io/module_dos/write_dos_pw.cpp @@ -20,7 +20,7 @@ void ModuleIO::write_dos_pw( const bool dos_setemin, const double dos_emin_ev, const bool two_fermi, - const bool out_app_flag, + const int out_app_flag, const int bndpar, const std::string& global_out_dir, std::ofstream& ofs_running) diff --git a/source/source_io/module_dos/write_dos_pw.h b/source/source_io/module_dos/write_dos_pw.h index 4b2417d8f42..2491c7d303a 100644 --- a/source/source_io/module_dos/write_dos_pw.h +++ b/source/source_io/module_dos/write_dos_pw.h @@ -27,7 +27,7 @@ namespace ModuleIO const bool dos_setemin, const double dos_emin_ev, const bool two_fermi, - const bool out_app_flag, + const int out_app_flag, const int bndpar, const std::string& global_out_dir, std::ofstream& ofs_running); diff --git a/source/source_io/module_energy/write_bands.cpp b/source/source_io/module_energy/write_bands.cpp index f796b19f07a..e5d229f00c0 100644 --- a/source/source_io/module_energy/write_bands.cpp +++ b/source/source_io/module_energy/write_bands.cpp @@ -89,7 +89,7 @@ void ModuleIO::nscf_bands(const int& is, const int band_group = Parallel::make_band_world().rank(); if (GlobalV::MY_RANK == 0) { - if (PARAM.inp.out_app_flag == true) + if (PARAM.inp.out_app_flag == 1) { // do nothing } @@ -149,7 +149,7 @@ void ModuleIO::nscf_bands(const int& is, if (GlobalV::MY_RANK == 0) { - if (PARAM.inp.out_app_flag == true) + if (PARAM.inp.out_app_flag == 1) { ofs.open(eig_file.c_str(), std::ios::app); } diff --git a/source/source_io/module_hs/dhs_sparse_writer.cpp b/source/source_io/module_hs/dhs_sparse_writer.cpp index 5c4a049546f..4d69c718a93 100644 --- a/source/source_io/module_hs/dhs_sparse_writer.cpp +++ b/source/source_io/module_hs/dhs_sparse_writer.cpp @@ -24,7 +24,7 @@ void ModuleIO::save_dH_sparse(const int& istep, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nspin, const int nlocal, std::ofstream& ofs_running) { @@ -112,7 +112,7 @@ void ModuleIO::save_dH_sparse(const int& istep, } } - const bool md_no_append = (calculation == "md") && !out_app_flag; + const bool md_no_append = (calculation == "md") && (out_app_flag != 1); for (auto& comp: comps) { for (int ispin = 0; ispin < 2; ++ispin) @@ -134,7 +134,7 @@ void ModuleIO::save_dH_sparse(const int& istep, if (GlobalV::DRANK == 0) { - const bool open_in_append = (calculation == "md") && out_app_flag && istep >= 0; + const bool open_in_append = (calculation == "md") && (out_app_flag == 1) && istep >= 0; for (auto& comp: comps) { const std::string label = std::string("dH") + comp.axis; diff --git a/source/source_io/module_hs/dhs_sparse_writer.h b/source/source_io/module_hs/dhs_sparse_writer.h index 8aab9c8007e..7d6337d577e 100644 --- a/source/source_io/module_hs/dhs_sparse_writer.h +++ b/source/source_io/module_hs/dhs_sparse_writer.h @@ -20,7 +20,7 @@ void save_dH_sparse(const int& istep, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nspin, const int nlocal, std::ofstream& ofs_running); diff --git a/source/source_io/module_hs/hs_sparse_io.cpp b/source/source_io/module_hs/hs_sparse_io.cpp index f645893164e..580903839cc 100644 --- a/source/source_io/module_hs/hs_sparse_io.cpp +++ b/source/source_io/module_hs/hs_sparse_io.cpp @@ -33,7 +33,7 @@ void open_sparse_file(std::ofstream& ofs, const ModuleIO::SparseWriteOptions& op { mode |= std::ios::binary; } - const bool append_on_restart = (options.calculation == "md") && options.out_app_flag && options.istep >= 0; + const bool append_on_restart = (options.calculation == "md") && (options.out_app_flag == 1) && options.istep >= 0; if (append_on_restart) { mode |= std::ios::app; diff --git a/source/source_io/module_hs/hs_sparse_io.h b/source/source_io/module_hs/hs_sparse_io.h index d8bdb089989..3371e78628f 100644 --- a/source/source_io/module_hs/hs_sparse_io.h +++ b/source/source_io/module_hs/hs_sparse_io.h @@ -31,7 +31,7 @@ struct SparseWriteOptions // Runtime flags that decide file-open mode (append on md restart). // Must be provided explicitly by the caller instead of reading PARAM. std::string calculation; - bool out_app_flag = false; + int out_app_flag = 0; }; template diff --git a/source/source_io/module_hs/hsk_writer.cpp b/source/source_io/module_hs/hsk_writer.cpp index 263635b573b..0faa1ba1aa7 100644 --- a/source/source_io/module_hs/hsk_writer.cpp +++ b/source/source_io/module_hs/hsk_writer.cpp @@ -14,7 +14,7 @@ void ModuleIO::write_hsk( hamilt::Hamilt* p_hamilt, const Parallel_Orbitals &pv, const bool gamma_only, - const bool out_app_flag, + const int out_app_flag, const int istep, const int out_type, const int precision, @@ -96,11 +96,11 @@ template void ModuleIO::write_hsk( const std::string&, const int, const int, const int, const std::vector&, const std::vector&, hamilt::Hamilt*, const Parallel_Orbitals&, - const bool, const bool, const int, const int, const int, + const bool, const int, const int, const int, const int, const int, const std::string&, const int, std::ofstream&); template void ModuleIO::write_hsk>( const std::string&, const int, const int, const int, const std::vector&, const std::vector&, hamilt::Hamilt>*, const Parallel_Orbitals&, - const bool, const bool, const int, const int, const int, + const bool, const int, const int, const int, const int, const int, const std::string&, const int, std::ofstream&); diff --git a/source/source_io/module_hs/hsk_writer.h b/source/source_io/module_hs/hsk_writer.h index af2bb6a8540..d80a5eb8839 100644 --- a/source/source_io/module_hs/hsk_writer.h +++ b/source/source_io/module_hs/hsk_writer.h @@ -25,7 +25,7 @@ void write_hsk(const std::string& global_out_dir, hamilt::Hamilt* p_hamilt, const Parallel_Orbitals& pv, const bool gamma_only, - const bool out_app_flag, + const int out_app_flag, const int istep, const int out_type, const int precision, diff --git a/source/source_io/module_hs/hsr_legacy.cpp b/source/source_io/module_hs/hsr_legacy.cpp index 0f1e37daa3e..d3c7d338d85 100644 --- a/source/source_io/module_hs/hsr_legacy.cpp +++ b/source/source_io/module_hs/hsr_legacy.cpp @@ -107,7 +107,7 @@ void ModuleIO::output_SR(Parallel_Orbitals& pv, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nspin, std::ofstream& ofs_running) { @@ -174,7 +174,7 @@ void ModuleIO::output_TR(const int istep, ModuleBase::timer::start("ModuleIO", "output_TR"); std::stringstream sst; - const bool md_no_append = (options.calculation == "md") && !options.out_app_flag; + const bool md_no_append = (options.calculation == "md") && (options.out_app_flag != 1); if (istep >= 0) { // insert the 1-indexed ionic-step suffix right after the "tr" prefix, @@ -220,7 +220,7 @@ template void ModuleIO::output_SR(Parallel_Orbitals& pv, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nspin, std::ofstream& ofs_running); template void ModuleIO::output_SR>(Parallel_Orbitals& pv, @@ -233,6 +233,6 @@ template void ModuleIO::output_SR>(Parallel_Orbitals& pv, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nspin, std::ofstream& ofs_running); diff --git a/source/source_io/module_hs/hsr_legacy.h b/source/source_io/module_hs/hsr_legacy.h index 334374125e1..cddd93aed3f 100644 --- a/source/source_io/module_hs/hsr_legacy.h +++ b/source/source_io/module_hs/hsr_legacy.h @@ -29,7 +29,7 @@ struct MatROutputOptions std::string global_out_dir; std::string global_matrix_dir; std::string calculation; - bool out_app_flag = false; + int out_app_flag = 0; int nspin = 1; }; @@ -82,7 +82,7 @@ void output_SR(Parallel_Orbitals& pv, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nspin, std::ofstream& ofs_running); diff --git a/source/source_io/module_hs/hterm_writer.cpp b/source/source_io/module_hs/hterm_writer.cpp index a4be8399b56..ee6c997c7b3 100644 --- a/source/source_io/module_hs/hterm_writer.cpp +++ b/source/source_io/module_hs/hterm_writer.cpp @@ -79,7 +79,7 @@ static void gather_and_write(const std::string& prefix, const int* iat2iwt, const int nat, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const std::string& global_out_dir, const std::string& global_matrix_dir, std::ofstream* ofs_running) @@ -94,7 +94,7 @@ static void gather_and_write(const std::string& prefix, if (GlobalV::MY_RANK == 0) #endif { - const bool md_no_append = (calculation == "md") && !out_app_flag; + const bool md_no_append = (calculation == "md") && (out_app_flag != 1); const std::string& out_dir = md_no_append ? global_matrix_dir : global_out_dir; const std::string fname = out_dir + hsr_gen_fname(prefix, ispin, append, istep); // H(R) component terms (T, V^NL, V^L, V^H, V^XC, V^EXX) are parts of @@ -127,7 +127,7 @@ static void write_hk_common(hamilt::HContainer& hR, const int nlocal, const bool gamma_only, const std::string& global_out_dir, - const bool out_app_flag, + const int out_app_flag, const std::string& ks_solver, const int drank) { @@ -200,7 +200,7 @@ static void write_h_term(WriteHParams& params, const HTermSpec& spec) const int nlocal = params.nlocal; const bool gamma_only = params.gamma_only_local; const std::string& global_out_dir = params.global_out_dir; - const bool out_app_flag = params.out_app_flag; + const int out_app_flag = params.out_app_flag; const int nspin_out = (nspin == 2 ? 2 : 1); @@ -378,7 +378,7 @@ static void write_h_exx_impl(const UnitCell& ucell, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const std::string& ks_solver, const int drank, const Exx_Info& exx_info, diff --git a/source/source_io/module_hs/hterm_writer.h b/source/source_io/module_hs/hterm_writer.h index 7aa2f721fc6..2cc9599548e 100644 --- a/source/source_io/module_hs/hterm_writer.h +++ b/source/source_io/module_hs/hterm_writer.h @@ -46,7 +46,7 @@ struct WriteHParams bool domag = false; bool domag_z = false; int gga_grad = 0; // used by the XC term - bool out_app_flag = false; + int out_app_flag = 0; std::string calculation; std::string global_out_dir; std::string global_matrix_dir; diff --git a/source/source_io/module_hs/output_mat_sparse.cpp b/source/source_io/module_hs/output_mat_sparse.cpp index 5179bd5477e..59e133574e7 100644 --- a/source/source_io/module_hs/output_mat_sparse.cpp +++ b/source/source_io/module_hs/output_mat_sparse.cpp @@ -25,7 +25,7 @@ void output_mat_sparse(const MatSparseOutputOptions& options, const std::string& global_out_dir = PARAM.globalv.global_out_dir; const std::string& global_matrix_dir = PARAM.globalv.global_matrix_dir; const std::string& calculation = PARAM.inp.calculation; - const bool out_app_flag = PARAM.inp.out_app_flag; + const int out_app_flag = PARAM.inp.out_app_flag; const int nspin = PARAM.inp.nspin; const bool gamma_only_local = PARAM.globalv.gamma_only_local; const int npol = PARAM.globalv.npol; diff --git a/source/source_io/module_hs/pos_op_mat.cpp b/source/source_io/module_hs/pos_op_mat.cpp index 04ec8d45e85..e37c7f1245e 100644 --- a/source/source_io/module_hs/pos_op_mat.cpp +++ b/source/source_io/module_hs/pos_op_mat.cpp @@ -89,7 +89,7 @@ void Position_op::out_rR(const UnitCell& ucell, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nlocal, const int npol, std::ofstream& ofs_running) diff --git a/source/source_io/module_hs/pos_op_mat.h b/source/source_io/module_hs/pos_op_mat.h index 96a6513c23e..d76d71e0c3e 100644 --- a/source/source_io/module_hs/pos_op_mat.h +++ b/source/source_io/module_hs/pos_op_mat.h @@ -76,7 +76,7 @@ class Position_op const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - const bool out_app_flag, + const int out_app_flag, const int nlocal, const int npol, std::ofstream& ofs_running); diff --git a/source/source_io/module_hs/pos_op_writer.cpp b/source/source_io/module_hs/pos_op_writer.cpp index 15e1fa201ea..f131047041b 100644 --- a/source/source_io/module_hs/pos_op_writer.cpp +++ b/source/source_io/module_hs/pos_op_writer.cpp @@ -22,7 +22,7 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - bool out_app_flag, + int out_app_flag, int nlocal, int npol, double sparse_threshold, @@ -216,7 +216,7 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, if (GlobalV::DRANK == 0) { std::stringstream ssr; - const bool md_no_append = (calculation == "md") && !out_app_flag; + const bool md_no_append = (calculation == "md") && (out_app_flag != 1); if (step >= 0) { ssr << (md_no_append ? global_matrix_dir : global_out_dir) << "rrg" << (step + 1) << "_nao.txt"; @@ -227,7 +227,7 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, } ofs_tem1.close(); - const bool open_in_append = (calculation == "md") && out_app_flag && step >= 0; + const bool open_in_append = (calculation == "md") && (out_app_flag == 1) && step >= 0; const int header_step = std::max(step, 0); ModuleIO::detail::assemble_csr(ssr.str(), tem1.str(), diff --git a/source/source_io/module_hs/pos_op_writer.h b/source/source_io/module_hs/pos_op_writer.h index 1fd22bcfc81..d2996e43890 100644 --- a/source/source_io/module_hs/pos_op_writer.h +++ b/source/source_io/module_hs/pos_op_writer.h @@ -35,7 +35,7 @@ class PosOpWriter const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, - bool out_app_flag, + int out_app_flag, int nlocal, int npol, double sparse_threshold, diff --git a/source/source_io/module_hs/vxc_op_lip.cpp b/source/source_io/module_hs/vxc_op_lip.cpp index fcee73b6cd4..094015992d4 100644 --- a/source/source_io/module_hs/vxc_op_lip.cpp +++ b/source/source_io/module_hs/vxc_op_lip.cpp @@ -197,7 +197,7 @@ void write_Vxc_LIP(int nspin, // add-up and write const int istep = -1; const int out_label = 1; // 1 means .txt while 2 means .dat - const bool out_app_flag = 0; + const int out_app_flag = 0; std::string vxc_file = ModuleIO::filename_output( global_out_dir, "vxc", "nao", ik, kv.ik2iktot, nspin, kv.get_nkstot(), diff --git a/source/source_io/module_hs/vxc_op_mat.cpp b/source/source_io/module_hs/vxc_op_mat.cpp index c7a0b8fe2b4..b749e110b5a 100644 --- a/source/source_io/module_hs/vxc_op_mat.cpp +++ b/source/source_io/module_hs/vxc_op_mat.cpp @@ -115,7 +115,7 @@ void write_Vxc(const int nspin, // write const int istep = -1; const int out_label = 1; // 1 means .txt while 2 means .dat - const bool out_app_flag = 0; + const int out_app_flag = 0; std::string vxc_file = ModuleIO::filename_output( global_out_dir, "vxc", "nao", ik, kv.ik2iktot, nspin, kv.get_nkstot(), diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index d86993b63d9..dde1c933695 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -483,8 +483,9 @@ struct Input_para bool out_hr_npz = false; ///< output H(R) matrix in npz format bool out_hsr_npz = false; ///< output H(R) and S(R) matrices in npz format bool out_dm_npz = false; ///< output DM(R) matrix in npz format - bool out_app_flag = true; ///< whether output r(R), H(R), S(R), T(R), and dH(R) matrices - ///< in an append manner during MD liuyu 2023-03-20 + int out_app_flag = 1; ///< 0: no append, no subdirectory; 1: append to same file; + ///< 2: write to subdirectory (PSI/, CHG/, POT/, ELF/, DOS/, DIP/, MAT/) + ///< liuyu 2023-03-20, modified 2025-10-16 int out_ndigits = 8; ///< Assuming 8 digits precision is needed for matrices output std::vector out_mat_t = {0, 8}; ///< output T(R) matrix with precision bool out_element_info = false; ///< output information of all elements diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index 80a55319466..dd600da3666 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -1394,12 +1394,22 @@ The circle order of the charge density on real space grids is: x is the outer lo item.annotation = "whether output r(R), H(R), S(R), T(R), and dH(R) " "matrices in an append manner during MD"; item.category = "Output information"; - item.type = "Boolean"; - item.description = "Whether to output r(R), H(R), S(R), T(R), dH(R), dS(R), and wfc matrices in an append manner during molecular dynamics calculations. Check input parameters out_mat_r, out_hsr, out_mat_t, out_mat_dh, out_hsk and out_wfc_lcao for more information."; - item.default_value = "true"; + item.type = "Integer"; + item.description = R"(Controls output file organization during multi-step calculations (MD, relax, RT-TDDFT). +* 0: Write to OUT directory directly, no append (files overwritten each step) +* 1: Append to same file (default, backward compatible) +* 2: Write to subdirectory (PSI/, CHG/, POT/, ELF/, DOS/, DIP/, MAT/) +For more information, refer to the documentation of out_mat_r, out_hsr, out_mat_t, out_mat_dh, out_hsk, out_wfc_lcao, out_chg, out_pot, out_elf, out_dos, out_dipole.)"; + item.default_value = "1"; item.unit = ""; item.set_availability("basis_type==lcao and gamma_only==0"); - read_sync_bool(input.out_app_flag); + read_sync_int(input.out_app_flag); + item.check_value = [](const Input_Item& item, const Parameter& para) { + if (para.input.out_app_flag < 0 || para.input.out_app_flag > 2) + { + ModuleBase::WARNING_QUIT("ReadInput", "out_app_flag should be 0, 1, or 2"); + } + }; this->add_item(item); } { diff --git a/source/source_io/module_parameter/read_set_globalv.cpp b/source/source_io/module_parameter/read_set_globalv.cpp index 3b395e6b8f5..94bfaeb06ac 100644 --- a/source/source_io/module_parameter/read_set_globalv.cpp +++ b/source/source_io/module_parameter/read_set_globalv.cpp @@ -97,6 +97,35 @@ void ReadInput::set_global_dir(const Input_para& inp, System_para& sys) sys.global_wfc_dir = sys.global_out_dir + "WFC/"; sys.global_wfc_dir = to_dir(sys.global_wfc_dir); + /// Set subdirectories based on out_app_flag + /// out_app_flag == 2: use unified uppercase subdirectories + /// otherwise: keep backward-compatible behavior (all point to global_out_dir) + if (inp.out_app_flag == 2) + { + sys.global_psi_dir = sys.global_out_dir + "PSI/"; + sys.global_chg_dir = sys.global_out_dir + "CHG/"; + sys.global_pot_dir = sys.global_out_dir + "POT/"; + sys.global_elf_dir = sys.global_out_dir + "ELF/"; + sys.global_dos_dir = sys.global_out_dir + "DOS/"; + sys.global_dip_dir = sys.global_out_dir + "DIP/"; + sys.global_mat_dir = sys.global_out_dir + "MAT/"; + } + else + { + sys.global_psi_dir = sys.global_out_dir; + sys.global_chg_dir = sys.global_out_dir; + sys.global_pot_dir = sys.global_out_dir; + sys.global_elf_dir = sys.global_out_dir; + sys.global_dos_dir = sys.global_out_dir; + sys.global_dip_dir = sys.global_out_dir; + sys.global_mat_dir = sys.global_out_dir; + } + + /// Map legacy directory names to new unified names + /// WFC -> PSI, matrix -> MAT + sys.global_wfc_dir = sys.global_psi_dir; + sys.global_matrix_dir = sys.global_mat_dir; + /// get the global ML KEDF descriptor directory sys.global_mlkedf_descriptor_dir = sys.global_out_dir + "MLKEDF_Descriptors/"; sys.global_mlkedf_descriptor_dir = to_dir(sys.global_mlkedf_descriptor_dir); @@ -154,6 +183,13 @@ void ReadInput::set_global_dir(const Input_para& inp, System_para& sys) Parallel_Common::bcast_string(sys.global_stru_dir); Parallel_Common::bcast_string(sys.global_matrix_dir); Parallel_Common::bcast_string(sys.global_wfc_dir); + Parallel_Common::bcast_string(sys.global_psi_dir); + Parallel_Common::bcast_string(sys.global_chg_dir); + Parallel_Common::bcast_string(sys.global_pot_dir); + Parallel_Common::bcast_string(sys.global_elf_dir); + Parallel_Common::bcast_string(sys.global_dos_dir); + Parallel_Common::bcast_string(sys.global_dip_dir); + Parallel_Common::bcast_string(sys.global_mat_dir); Parallel_Common::bcast_string(sys.global_in_stru); #endif } diff --git a/source/source_io/module_parameter/system_parameter.h b/source/source_io/module_parameter/system_parameter.h index 438bd32c145..4705c33f4ea 100644 --- a/source/source_io/module_parameter/system_parameter.h +++ b/source/source_io/module_parameter/system_parameter.h @@ -53,8 +53,16 @@ struct System_para std::string global_stru_dir = ""; ///< global structure directory std::string global_matrix_dir = ""; ///< global matrix directory std::string global_wfc_dir = ""; ///< global wavefunction directory + // Subdirectories used when out_app_flag == 2 + std::string global_psi_dir = ""; ///< wavefunction directory (PSI/) + std::string global_chg_dir = ""; ///< charge density directory (CHG/) + std::string global_pot_dir = ""; ///< potential directory (POT/) + std::string global_elf_dir = ""; ///< ELF directory (ELF/) + std::string global_dos_dir = ""; ///< DOS directory (DOS/) + std::string global_dip_dir = ""; ///< dipole directory (DIP/) + std::string global_mat_dir = ""; ///< matrix directory (MAT/) std::string global_mlkedf_descriptor_dir = ""; ///< global ML KEDF descriptor directory - std::string global_deepks_label_elec_dir = ""; ///< global directory for DeePKS labels during electronic steps + std::string global_deepks_label_elec_dir = ""; ///< global directory for DeePKS labels during electronic steps std::string log_file = "log"; ///< log file name bool deepks_setorb = false; ///< true if "deepks" is set diff --git a/source/source_io/module_wf/read_wf2rho_pw.cpp b/source/source_io/module_wf/read_wf2rho_pw.cpp index 50e31e733a3..298334607db 100644 --- a/source/source_io/module_wf/read_wf2rho_pw.cpp +++ b/source/source_io/module_wf/read_wf2rho_pw.cpp @@ -129,7 +129,7 @@ void ModuleIO::read_wf2rho_pw_impl(const ModulePW::PW_Basis_K* pw_wfc, // mohan add 2025-05-17 // .dat file const int out_type = 2; - const bool out_app_flag = false; + const int out_app_flag = 0; const bool gamma_only = false; const int istep = -1; diff --git a/source/source_io/module_wf/read_wfc_nao.cpp b/source/source_io/module_wf/read_wfc_nao.cpp index 3148e2c0e2f..58599769d4c 100644 --- a/source/source_io/module_wf/read_wfc_nao.cpp +++ b/source/source_io/module_wf/read_wfc_nao.cpp @@ -296,7 +296,7 @@ bool ModuleIO::read_wfc_nao( { if (myrank == 0) { - const bool out_app_flag = false; + const int out_app_flag = 0; std::stringstream error_message; std::string readin_dir = global_readin_dir; if(istep >= 0) diff --git a/source/source_io/module_wf/write_wfc_nao.cpp b/source/source_io/module_wf/write_wfc_nao.cpp index 2d28e96e60d..b468cc4c6c1 100644 --- a/source/source_io/module_wf/write_wfc_nao.cpp +++ b/source/source_io/module_wf/write_wfc_nao.cpp @@ -202,7 +202,7 @@ void wfc_nao_write2file_complex(const std::string& name, template void write_wfc_nao(const int out_type, - const bool out_app_flag, + const int out_app_flag, const psi::Psi& psi, const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, @@ -270,7 +270,7 @@ void write_wfc_nao(const int out_type, if (myid == 0) { std::string wfc_dir; - if(out_app_flag) + if(out_app_flag == 1) { wfc_dir = PARAM.globalv.global_out_dir; } @@ -281,7 +281,7 @@ void write_wfc_nao(const int out_type, std::string fn = filename_output(wfc_dir,"wf","nao",ik,ik2iktot,nspin,nkstot, out_type,out_app_flag,gamma_only,istep); - bool append_flag = (istep > 0 && out_app_flag); + bool append_flag = (istep > 0 && out_app_flag == 1); if (std::is_same::value) { wfc_nao_write2file(fn, @@ -311,7 +311,7 @@ void write_wfc_nao(const int out_type, } template void write_wfc_nao(const int out_type, - const bool out_app_flag, + const int out_app_flag, const psi::Psi& psi, const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, @@ -323,7 +323,7 @@ template void write_wfc_nao(const int out_type, const int istep); template void write_wfc_nao>(const int out_type, - const bool out_app_flag, + const int out_app_flag, const psi::Psi>& psi, const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, diff --git a/source/source_io/module_wf/write_wfc_nao.h b/source/source_io/module_wf/write_wfc_nao.h index 152723b5109..70dde0ae6fb 100644 --- a/source/source_io/module_wf/write_wfc_nao.h +++ b/source/source_io/module_wf/write_wfc_nao.h @@ -25,7 +25,7 @@ namespace ModuleIO */ template void write_wfc_nao(const int out_type, - const bool out_app_flag, + const int out_app_flag, const psi::Psi& psi, const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, diff --git a/source/source_io/module_wf/write_wfc_pw.cpp b/source/source_io/module_wf/write_wfc_pw.cpp index 450bb596988..d90f7f5d645 100644 --- a/source/source_io/module_wf/write_wfc_pw.cpp +++ b/source/source_io/module_wf/write_wfc_pw.cpp @@ -43,7 +43,7 @@ void ModuleIO::write_wfc_pw( assert(nkstot>0); assert(nks>0); - bool out_app_flag = false; // need to modify later, mohan 2025-05-17 + int out_app_flag = 0; // need to modify later, mohan 2025-05-17 bool gamma_only = false; // need to modify later, mohan 2025-05-17 std::string* wfilename = new std::string[nks]; diff --git a/source/source_psi/psi_init_file.cpp b/source/source_psi/psi_init_file.cpp index 29a8619e30b..cc7086d8c8f 100644 --- a/source/source_psi/psi_init_file.cpp +++ b/source/source_psi/psi_init_file.cpp @@ -52,7 +52,7 @@ void psi_init_file::init_psig(T* psig, const int& ik) // mohan update, this is for plane wave, 2025-05-17 const int out_type = 2; - const bool out_app_flag = false; + const int out_app_flag = 0; const bool gamma_only = false; const int istep = -1; From 2545b1be41ac0887002d51c3087da59fbda1e0a3 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 15:00:14 +0800 Subject: [PATCH 02/30] feat(output): use unified subdirectories when out_app_flag=2 - ctrl_output_fp.cpp: route chg/tau to global_chg_dir, pot/potes to global_pot_dir, ELF to global_elf_dir, dipole to global_dip_dir - ctrl_output_pw.cpp: route wfc to global_psi_dir, dos to global_dos_dir, ldos to global_dos_dir - ctrl_scf_lcao.cpp: route dos to global_dos_dir - ctrl_runner_lcao.cpp: route ldos to global_dos_dir - read_input.cpp: create subdirectories (PSI/CHG/POT/ELF/DOS/DIP/MAT) when out_app_flag==2 - Update docs: out_app_flag semantics from bool to int (0/1/2) Issue: #8151 --- docs/advanced/elec_properties/hs_matrix.md | 8 +++--- .../elec_properties/position_matrix.md | 2 +- docs/advanced/elec_properties/wfc.md | 2 +- docs/advanced/scf/initialization.md | 2 +- .../source_io/module_ctrl/ctrl_output_fp.cpp | 16 ++++++----- .../source_io/module_ctrl/ctrl_output_pw.cpp | 6 ++--- .../module_ctrl/ctrl_runner_lcao.cpp | 2 +- .../source_io/module_ctrl/ctrl_scf_lcao.cpp | 2 +- .../source_io/module_parameter/read_input.cpp | 27 +++++++++++++++++++ 9 files changed, 49 insertions(+), 18 deletions(-) diff --git a/docs/advanced/elec_properties/hs_matrix.md b/docs/advanced/elec_properties/hs_matrix.md index 5adf14e3971..6e4aa48423b 100644 --- a/docs/advanced/elec_properties/hs_matrix.md +++ b/docs/advanced/elec_properties/hs_matrix.md @@ -39,7 +39,7 @@ The output filenames depend on the k-point algorithm and `nspin`: Here `${k}` is the one-based k-point index. For `nspin = 2`, the overlap matrix is spin-independent, so only one overlap file is written for each physical k point. The gamma-only algorithm does not support `nspin = 4`; use the multi-k algorithm with an explicit Gamma-only `KPT` file for a noncollinear calculation at Gamma. -When `out_app_flag` is false, `g${step}` is inserted before `_nao`, where `${step}` is the one-based ionic-step index. For example, the first spin channel at the first k point and first ionic step is written to `hk1s1g1_nao.txt`. +When `out_app_flag` is not 1, `g${step}` is inserted before `_nao`, where `${step}` is the one-based ionic-step index. For example, the first spin channel at the first k point and first ionic step is written to `hk1s1g1_nao.txt`. Each output block starts with a comment header containing the one-based ionic-step index, filename, `gamma only` flag, and matrix dimensions. It is followed by `Row 1`, `Row 2`, and so on. Each row contains the matrix elements from the diagonal through the upper triangle. @@ -57,7 +57,7 @@ For `out_hsk 2`, the filenames in the table above use `.dat` instead of `.txt`. The file therefore contains `sizeof(int) + N(N+1)/2 * sizeof(double)` bytes for a gamma-only record and `sizeof(int) + N(N+1) * sizeof(double)` bytes for a multi-k or spinor record. The format uses the host integer representation and byte order and is intended for readers using a compatible ABI. -When `out_app_flag` is true, the first ionic step truncates the shared file and every later step appends another complete record. When it is false, each ionic step has a separate filename containing `g${step}` before `_nao`. +When `out_app_flag` is 1, the first ionic step truncates the shared file and every later step appends another complete record. When it is not 1, each ionic step has a separate filename containing `g${step}` before `_nao`. ## out_hsr @@ -95,7 +95,7 @@ In gamma-only mode, every generated file reports one Bravais lattice vector and ### Native Binary CSR Format -Set `out_hsr 2` to write the same H(R) and S(R) matrix sets with a `.dat` suffix. For example, an `nspin = 2` calculation writes `hrs1_nao.dat`, `hrs2_nao.dat`, and `sr_nao.dat`. When `out_app_flag` is false, the one-based ionic step is included before `_nao`, for example `hrs1g1_nao.dat` and `srg1_nao.dat`. +Set `out_hsr 2` to write the same H(R) and S(R) matrix sets with a `.dat` suffix. For example, an `nspin = 2` calculation writes `hrs1_nao.dat`, `hrs2_nao.dat`, and `sr_nao.dat`. When `out_app_flag` is not 1, the one-based ionic step is included before `_nao`, for example `hrs1g1_nao.dat` and `srg1_nao.dat`. Each ionic step is a complete record with no padding or self-describing header: @@ -107,7 +107,7 @@ Each ionic step is a complete record with no padding or self-describing header: All R blocks stored by the internal HContainer are present, including blocks with zero nonzero values. The sparse threshold is `1e-10`, matching text CSR output. The format uses the host integer representation and byte order and therefore requires a compatible ABI. It does not contain unit-cell, spin, or matrix-label metadata; those are determined by the calculation input and filename. -When `out_app_flag` is true, the first ionic step truncates the shared file and later ionic steps append complete records. Otherwise every ionic step is written to its own file. +When `out_app_flag` is 1, the first ionic step truncates the shared file and later ionic steps append complete records. Otherwise every ionic step is written to its own file. ### NPZ Format diff --git a/docs/advanced/elec_properties/position_matrix.md b/docs/advanced/elec_properties/position_matrix.md index c504a866b64..38743d5b65b 100644 --- a/docs/advanced/elec_properties/position_matrix.md +++ b/docs/advanced/elec_properties/position_matrix.md @@ -18,7 +18,7 @@ Each file or each section of the appended file starts with "STEP: " followed by Each block here contains the matrix for the corresponding cell. There are three columns in each block, giving the matrix elements in x, y, z directions, respectively. There are altogether nbasis * nbasis lines in each block, which emulates the matrix elements. -In molecular dynamics (MD) calculations, if [out_app_flag](../input_files/input-main.md#out_app_flag) is set to true, then `data-rR-tr` is written in an append manner. Otherwise, output files will be put in a separate directory, `matrix`, and named as `$x`_data-rR-tr, where `$x` is the number of MD step. In addition, the output frequency is controlled by [out_freq_ion](../input_files/input-main.md#out_freq_ion). For example, if we are running a 10-step MD with out_freq_ion = 3, then `$x` will be 0, 3, 6, and 9. +In molecular dynamics (MD) calculations, if [out_app_flag](../input_files/input-main.md#out_app_flag) is set to 1, then `data-rR-tr` is written in an append manner. Otherwise, output files will be put in a separate directory, `matrix`, and named as `$x`_data-rR-tr, where `$x` is the number of MD step. In addition, the output frequency is controlled by [out_freq_ion](../input_files/input-main.md#out_freq_ion). For example, if we are running a 10-step MD with out_freq_ion = 3, then `$x` will be 0, 3, 6, and 9. ## get_s We also offer the option of only calculating the position matrix without running SCF. For that purpose, in `INPUT` file we need to set the keyword [calculation](../input_files/input-main.md#calculation) to `get_s`, and [out_mat_r](../input_files/input-main.md#out_mat_r) to `true`. diff --git a/docs/advanced/elec_properties/wfc.md b/docs/advanced/elec_properties/wfc.md index 354d8844fc4..562b0c4b32b 100644 --- a/docs/advanced/elec_properties/wfc.md +++ b/docs/advanced/elec_properties/wfc.md @@ -18,7 +18,7 @@ The [`init_wfc file binary`](../scf/initialization.md#wavefunction) path reads o For `basis_type=lcao`, [`out_wfc_lcao`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#out-wfc-lcao) controls the output of numerical-atomic-orbital coefficients: `1` writes text files with the `.txt` suffix and `2` writes binary files with the `.dat` suffix. Multi-k calculations generate `wfs{spin}k{k-point}_nao.txt` or `.dat`, while Gamma-only calculations generate `wfs{spin}_nao.txt` or `.dat`. -To reuse LCAO coefficients, point `read_file_dir` to the coefficient files and set `init_wfc file txt` for text input or `init_wfc file binary` for binary input. The selected extension is required; there is no automatic format fallback. Only independent files without geometry-step indices are supported, not files accumulated with `out_app_flag`. +To reuse LCAO coefficients, point `read_file_dir` to the coefficient files and set `init_wfc file txt` for text input or `init_wfc file binary` for binary input. The selected extension is required; there is no automatic format fallback. Only independent files without geometry-step indices are supported, not files accumulated with `out_app_flag=1`. ## Wave Function in Real Space diff --git a/docs/advanced/scf/initialization.md b/docs/advanced/scf/initialization.md index 427146bf223..4517b691b44 100644 --- a/docs/advanced/scf/initialization.md +++ b/docs/advanced/scf/initialization.md @@ -26,7 +26,7 @@ For `basis_type=pw`, the available options are: * `nao+random`: Apply an approximately 5% multiplicative random perturbation to the NAO initialization; any bands not covered by NAO states are first initialized randomly. * `file binary`: Read binary `wf*_pw.dat` files generated with [`out_wfc_pw=2`](../elec_properties/wfc.md) from `read_file_dir`. The files must match the current k points, `nbands`, plane-wave layout, and lattice. The `txt` format is not supported for PW wavefunctions. -For `basis_type=lcao`, use `init_wfc file txt` to read text `wf*_nao.txt` files generated with [`out_wfc_lcao=1`](../elec_properties/wfc.md), or `init_wfc file binary` to read binary `wf*_nao.dat` files generated with `out_wfc_lcao=2`, from `read_file_dir`. LCAO wavefunctions initialize the density matrix and real-space charge density. The selected format is required; ABACUS does not automatically detect or fall back to the other format. The files must use a compatible NAO basis, match the current k-point and spin setup, and contain enough bands. Only independent files without geometry-step indices are supported, not files accumulated with `out_app_flag`. +For `basis_type=lcao`, use `init_wfc file txt` to read text `wf*_nao.txt` files generated with [`out_wfc_lcao=1`](../elec_properties/wfc.md), or `init_wfc file binary` to read binary `wf*_nao.dat` files generated with `out_wfc_lcao=2`, from `read_file_dir`. LCAO wavefunctions initialize the density matrix and real-space charge density. The selected format is required; ABACUS does not automatically detect or fall back to the other format. The files must use a compatible NAO basis, match the current k-point and spin setup, and contain enough bands. Only independent files without geometry-step indices are supported, not files accumulated with `out_app_flag=1`. For `basis_type=lcao_in_pw`, `init_wfc` is automatically set to `nao`. diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index c6c08cc9a39..7fe1a6e0aad 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -36,6 +36,10 @@ void ctrl_output_fp(UnitCell& ucell, const bool gamma_only = PARAM.globalv.gamma_only_local; const int nspin = inp.nspin; const std::string global_out_dir = PARAM.globalv.global_out_dir; + const std::string global_chg_dir = PARAM.globalv.global_chg_dir; + const std::string global_pot_dir = PARAM.globalv.global_pot_dir; + const std::string global_elf_dir = PARAM.globalv.global_elf_dir; + const std::string global_dip_dir = PARAM.globalv.global_dip_dir; const int output_frequency = inp.esolver_type == "tddft" ? inp.out_freq_td : inp.out_freq_ion; // Print the global electronic step for TDDFT and the ionic step otherwise. @@ -61,7 +65,7 @@ void ctrl_output_fp(UnitCell& ucell, { for (int is = 0; is < nspin; ++is) { - std::string fn = PARAM.globalv.global_out_dir + "chg"; + std::string fn = PARAM.globalv.global_chg_dir + "chg"; std::string spin_block; if (nspin == 2 || nspin == 4) @@ -94,7 +98,7 @@ void ctrl_output_fp(UnitCell& ucell, if (XC_Functional::get_ked_flag()) { - fn = PARAM.globalv.global_out_dir + "tau"; + fn = PARAM.globalv.global_chg_dir + "tau"; fn += spin_block + geom_block + ".cube"; @@ -123,7 +127,7 @@ void ctrl_output_fp(UnitCell& ucell, { for (int is = 0; is < nspin; is++) { - std::string fn = PARAM.globalv.global_out_dir + "pot"; + std::string fn = PARAM.globalv.global_pot_dir + "pot"; std::string spin_block; if (nspin == 2 || nspin == 4) @@ -157,7 +161,7 @@ void ctrl_output_fp(UnitCell& ucell, } else if (inp.out_pot[0] == 2 && should_output) { - std::string fn = PARAM.globalv.global_out_dir + "potes"; + std::string fn = PARAM.globalv.global_pot_dir + "potes"; fn += geom_block + ".cube"; const std::vector* pcc_potential = pelec->pot->pcc_electron_potential(); @@ -189,7 +193,7 @@ void ctrl_output_fp(UnitCell& ucell, module_charge::cal_rhog_symm(is, chr, pw_rhod, ucell.symm); } - std::string out_dir = PARAM.globalv.global_out_dir; + std::string out_dir = PARAM.globalv.global_elf_dir; ModuleIO::write_elf(out_dir, istep, nspin, @@ -226,7 +230,7 @@ void ctrl_output_fp(UnitCell& ucell, for (int is = 0; is < nspin; ++is) { std::stringstream ss_dipole; - ss_dipole << global_out_dir << "dipole_s" << is + 1 << ".txt"; + ss_dipole << global_dip_dir << "dipole_s" << is + 1 << ".txt"; ModuleIO::write_dipole(ucell, chr.rho[is], pw_rhod, istep, ss_dipole.str(), ofs_running); } } diff --git a/source/source_io/module_ctrl/ctrl_output_pw.cpp b/source/source_io/module_ctrl/ctrl_output_pw.cpp index 8294d7cbae6..6856a49458e 100644 --- a/source/source_io/module_ctrl/ctrl_output_pw.cpp +++ b/source/source_io/module_ctrl/ctrl_output_pw.cpp @@ -74,7 +74,7 @@ void ModuleIO::ctrl_iter_pw(const int istep, GlobalV::NPROC_IN_POOL, inp.out_wfc_pw, inp.ecutwfc, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_psi_dir, stp.psi_cpu[0], kv, pw_wfc, @@ -174,7 +174,7 @@ void ModuleIO::ctrl_scf_pw(const int istep, PARAM.globalv.two_fermi, inp.out_app_flag, inp.bndpar, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_dos_dir, ofs_running); } } @@ -294,7 +294,7 @@ void ModuleIO::ctrl_runner_pw(UnitCell& ucell, inp.out_ldos, inp.stm_bias, inp.nspin, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_dos_dir, PARAM.globalv.two_fermi, inp.nbands, inp.dos_edelta_ev, diff --git a/source/source_io/module_ctrl/ctrl_runner_lcao.cpp b/source/source_io/module_ctrl/ctrl_runner_lcao.cpp index 5ceec5f9165..55a21bce7f7 100644 --- a/source/source_io/module_ctrl/ctrl_runner_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_runner_lcao.cpp @@ -54,7 +54,7 @@ void ctrl_runner_lcao(UnitCell& ucell, // unitcell { ModuleIO::Cal_ldos::cal_ldos_lcao(pelec->eferm, chr, dmat, kv, pelec->ekb, pelec->wg, psi[0], pgrid, pv, gd, ucell, - inp.stm_bias, inp.nspin, PARAM.globalv.global_out_dir, + inp.stm_bias, inp.nspin, PARAM.globalv.global_dos_dir, PARAM.globalv.two_fermi, inp.out_ldos[1], ofs_running); } diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index 7bd751b1a88..0be22ccc0dc 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -168,7 +168,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, inp.dos_emin_ev, PARAM.globalv.two_fermi, inp.bndpar, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_dos_dir, ofs_running); } diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index 2de46279d8b..15d6b28c52a 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -13,6 +13,7 @@ #include #include #include "source_base/formatter.h" +#include "source_base/fs_compat.h" #include "source_base/global_file.h" #include "source_base/global_function.h" #include "source_base/tool_quit.h" @@ -306,6 +307,32 @@ void ReadInput::create_directory(const Parameter& param) param.globalv.log_file, param.input.of_ml_gene_data, param.input.deepks_out_freq_elec > 0); // xiaohui add 2013-09-01 + + // Create unified subdirectories when out_app_flag == 2 + if (param.input.out_app_flag == 2) + { + const std::vector subdirs = { + param.globalv.global_psi_dir, + param.globalv.global_chg_dir, + param.globalv.global_pot_dir, + param.globalv.global_elf_dir, + param.globalv.global_dos_dir, + param.globalv.global_dip_dir, + param.globalv.global_mat_dir + }; + for (const auto& dir : subdirs) + { + if (!dir.empty() && dir != param.globalv.global_out_dir) + { + int ret = ModuleBase::make_directory(dir); + if (ret != 0 && errno != EEXIST) + { + std::cout << " CAN NOT MAKE THE DIR: " << dir << std::endl; + } + } + } + } + //const std::string ss = "test -d " + PARAM.inp.read_file_dir; struct stat st; if (stat(PARAM.inp.read_file_dir.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) From 1beabad0cd45192b96a76a234dce72e7e2973cdc Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 15:04:38 +0800 Subject: [PATCH 03/30] test(io): update directory expectations for out_app_flag default When out_app_flag defaults to 1 (not 2), all new unified directories (PSI, CHG, POT, ELF, DOS, DIP, MAT) map to global_out_dir for backward compatibility. Update io_sys_var_test.cpp expectations: - global_matrix_dir: "OUT.test/matrix/" -> "OUT.test/" - Add assertions for all 7 new directory variables --- source/source_io/test_serial/io_sys_var_test.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/source/source_io/test_serial/io_sys_var_test.cpp b/source/source_io/test_serial/io_sys_var_test.cpp index 678ee6a81b4..7acb032a557 100644 --- a/source/source_io/test_serial/io_sys_var_test.cpp +++ b/source/source_io/test_serial/io_sys_var_test.cpp @@ -68,7 +68,16 @@ TEST_F(InputTest, Item_test) EXPECT_EQ(sys.global_out_dir, "OUT.test/"); EXPECT_EQ(sys.global_stru_dir, "OUT.test/STRU/"); - EXPECT_EQ(sys.global_matrix_dir, "OUT.test/matrix/"); + // out_app_flag defaults to 1, so matrix dir maps to global out dir + EXPECT_EQ(sys.global_matrix_dir, "OUT.test/"); + EXPECT_EQ(sys.global_wfc_dir, "OUT.test/"); + EXPECT_EQ(sys.global_psi_dir, "OUT.test/"); + EXPECT_EQ(sys.global_chg_dir, "OUT.test/"); + EXPECT_EQ(sys.global_pot_dir, "OUT.test/"); + EXPECT_EQ(sys.global_elf_dir, "OUT.test/"); + EXPECT_EQ(sys.global_dos_dir, "OUT.test/"); + EXPECT_EQ(sys.global_dip_dir, "OUT.test/"); + EXPECT_EQ(sys.global_mat_dir, "OUT.test/"); TestParameters::set_globalv(readinput, input, sys); From a4ce9a3d9704e1dc66b9c018985b42d149c9d88c Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 15:10:31 +0800 Subject: [PATCH 04/30] refactor(io): migrate remaining out_app_flag bool usage to int - read_input.cpp: change !out_app_flag to out_app_flag == 0 for matrix/WFC legacy directory decisions - Update three unit tests to use int out_app_flag (0/1) instead of bool (false/true), matching the new function signatures: test_cal_dos.cpp, test_write_dos_pw.cpp, write_wfc_nao_test.cpp - Update two comments to describe 0/1 semantics --- code_quality_score.txt | 66 +++++++++++++++++++ .../module_dos/unittests/test_cal_dos.cpp | 6 +- .../unittests/test_write_dos_pw.cpp | 4 +- .../source_io/module_parameter/read_input.cpp | 4 +- source/source_io/test/write_wfc_nao_test.cpp | 12 ++-- 5 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 code_quality_score.txt diff --git a/code_quality_score.txt b/code_quality_score.txt new file mode 100644 index 00000000000..9a72ceadb64 --- /dev/null +++ b/code_quality_score.txt @@ -0,0 +1,66 @@ +====================================================================== +Code Quality Score Report +====================================================================== + +Per-module rollup (sorted by average score, worst first): + Module Files Avg Pass +---------------------------------------------------------------------- + source/source_io 4 55.0 1/4 + + Score File +---------------------------------------------------------------------- + 36 source/source_io/module_parameter/read_input.cpp + 54 source/source_io/test/write_wfc_nao_test.cpp + 58 source/source_io/module_dos/unittests/test_cal_dos.cpp + 72 source/source_io/module_dos/unittests/test_write_dos_pw.cpp + +---------------------------------------------------------------------- +File: source/source_io/module_parameter/read_input.cpp (score: 36) +---------------------------------------------------------------------- + [file_too_long] file: file has 755 lines (exceeds 500 by 255) (-12) + [uppercase_constant] file: 2 occurrence(s) (-2) + [global_dependency] file: 3 occurrence(s) (-9) + [auto_keyword] file: 13 occurrence(s) (capped at 10) (-10) + [high_cyclomatic_complexity] line 99: function 'filter_nonascii_and_comment' has cyclomatic complexity 11 (exceeds 10 by 1) (-1) + [high_cyclomatic_complexity] line 270: function 'create_directory' has cyclomatic complexity 17 (exceeds 10 by 7) (-7) + [high_cyclomatic_complexity] line 353: function 'read_txt_input' has cyclomatic complexity 17 (exceeds 10 by 7) (-7) + [high_cyclomatic_complexity] line 555: function 'write_txt_input' has cyclomatic complexity 21 (exceeds 10 by 11) (-11) + [high_cyclomatic_complexity] line 651: function 'check_ntype' has cyclomatic complexity 15 (exceeds 10 by 5) (-5) + +---------------------------------------------------------------------- +File: source/source_io/test/write_wfc_nao_test.cpp (score: 54) +---------------------------------------------------------------------- + [file_too_long] file: file has 586 lines (exceeds 500 by 86) (-4) + [tab_indentation] file: 21 occurrence(s) (capped at 5) (-5) + [line_too_long] file: 1 occurrence(s) (-1) + [uppercase_constant] file: 67 occurrence(s) (capped at 5) (-5) + [global_dependency] file: 13 occurrence(s) (capped at 10) (-30) + [member_function_too_long] line 157: member function in class WriteWfcLcaoTest spans 57 lines (exceeds 50) (-1) + +---------------------------------------------------------------------- +File: source/source_io/module_dos/unittests/test_cal_dos.cpp (score: 58) +---------------------------------------------------------------------- + [tab_indentation] file: 112 occurrence(s) (capped at 5) (-5) + [line_too_long] file: 3 occurrence(s) (-3) + [uppercase_constant] file: 28 occurrence(s) (capped at 5) (-5) + [global_dependency] file: 9 occurrence(s) (-27) + [duplicate_doc_block] file: 2 occurrence(s) (-2) + +---------------------------------------------------------------------- +File: source/source_io/module_dos/unittests/test_write_dos_pw.cpp (score: 72) +---------------------------------------------------------------------- + [tab_indentation] file: 114 occurrence(s) (capped at 5) (-5) + [line_too_long] file: 2 occurrence(s) (-2) + [uppercase_constant] file: 25 occurrence(s) (capped at 5) (-5) + [global_dependency] file: 4 occurrence(s) (-12) + [unpaired_new_delete] file: 1 occurrence(s) (-1) + [raw_new_keyword] file: 1 occurrence(s) (-1) + [duplicate_doc_block] file: 1 occurrence(s) (-1) + [member_local_name_conflict] line 35: local variable 'kv' in class DosPWTest shadows a member variable (-1) + +====================================================================== +Files scanned: 4 +Files shown: 4 +Average score: 55.0 +Passing (>= 60): 1/4 +====================================================================== diff --git a/source/source_io/module_dos/unittests/test_cal_dos.cpp b/source/source_io/module_dos/unittests/test_cal_dos.cpp index abcf942bad4..3fe5d410600 100644 --- a/source/source_io/module_dos/unittests/test_cal_dos.cpp +++ b/source/source_io/module_dos/unittests/test_cal_dos.cpp @@ -37,7 +37,7 @@ TEST_F(DosTest,Dos) EXPECT_EQ(dosp.is,0); const int istep = 1; - const bool out_app_flag = false; + const int out_app_flag = 0; const int bndpar = 1; ModuleIO::cal_dos(dosp.is, dosp.fa, @@ -124,7 +124,7 @@ TEST_F(DosTest,DosW1) GlobalV::ofs_warning.open("warning1.log"); const int istep = 1; - const bool out_app_flag = false; + const int out_app_flag = 0; const int bndpar = 1; EXPECT_NO_THROW(ModuleIO::cal_dos(dosp.is, dosp.fa, @@ -171,7 +171,7 @@ TEST_F(DosTest,DosW2) GlobalV::ofs_warning.open("warning2.log"); const int istep = 1; - const bool out_app_flag = false; + const int out_app_flag = 0; const int bndpar = 1; EXPECT_NO_THROW(ModuleIO::cal_dos(dosp.is, dosp.fa, diff --git a/source/source_io/module_dos/unittests/test_write_dos_pw.cpp b/source/source_io/module_dos/unittests/test_write_dos_pw.cpp index 384af45ac63..b866c0b24d6 100644 --- a/source/source_io/module_dos/unittests/test_write_dos_pw.cpp +++ b/source/source_io/module_dos/unittests/test_write_dos_pw.cpp @@ -67,7 +67,7 @@ TEST_F(DosPWTest,Dos1) const bool dos_setemax = true; const bool dos_setemin = true; const bool two_fermi = false; - const bool out_app_flag = false; + const int out_app_flag = 0; const int bndpar = 1; const std::string global_out_dir = "./"; @@ -144,7 +144,7 @@ TEST_F(DosPWTest,Dos2) const int nspin = 1; const int out_dos = 1; const bool two_fermi = false; - const bool out_app_flag = false; + const int out_app_flag = 0; const int bndpar = 1; const std::string global_out_dir = "./"; const bool dos_setemax = false; diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index 15d6b28c52a..1eca7302fec 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -277,7 +277,7 @@ void ReadInput::create_directory(const Parameter& param) // NAME : Run::make_dir( dir name : OUT.suffix) //---------------------------------------------------------- bool out_dir = false; - if (!param.input.out_app_flag + if (param.input.out_app_flag == 0 && ((param.input.out_hsr[0] == 1 || param.input.out_hsr[0] == 2) || param.input.out_mat_r[0] || param.input.out_mat_t[0] || param.input.out_mat_dh[0] || param.input.out_mat_ds[0])) @@ -285,7 +285,7 @@ void ReadInput::create_directory(const Parameter& param) out_dir = true; } bool out_wfc_dir = false; - if (param.input.out_wfc_lcao && !param.input.out_app_flag) + if (param.input.out_wfc_lcao && param.input.out_app_flag == 0) { out_wfc_dir = true; } diff --git a/source/source_io/test/write_wfc_nao_test.cpp b/source/source_io/test/write_wfc_nao_test.cpp index b5a64f2a336..8d7bc6b6a48 100644 --- a/source/source_io/test/write_wfc_nao_test.cpp +++ b/source/source_io/test/write_wfc_nao_test.cpp @@ -35,9 +35,9 @@ TEST(GenWfcLcaoFnameTest, OutType1GammaOnlyOutAppFlagTrue) const int nspin = 1; const int nkstot = 1; const int out_type = 1; - const bool out_app_flag = true; + const int out_app_flag = 1; const bool gamma_only = true; - // if out_app_flag = true, then the 'g' label will not show up + // if out_app_flag = 1 (append), then the 'g' label will not show up const int istep = 0; std::string result = ModuleIO::filename_output( @@ -60,9 +60,9 @@ TEST(GenWfcLcaoFnameTest, OutType2GammaOnlyOutAppFlagFalse) const int nspin = 2; const int nkstot = 2; const int out_type = 2; - const bool out_app_flag = false; + const int out_app_flag = 0; const bool gamma_only = true; - // if out_app_flag = false, then the 'g' label appears + // if out_app_flag = 0 (no append), then the 'g' label appears const int istep = 2; std::string result = ModuleIO::filename_output( @@ -84,7 +84,7 @@ TEST(GenWfcLcaoFnameTest, OutTypeInvalid) const int nspin = 1; const int nkstot = 3; const int out_type = 3; - const bool out_app_flag = true; + const int out_app_flag = 1; const bool gamma_only = false; const int istep = 3; @@ -151,7 +151,7 @@ class WriteWfcLcaoTest : public testing::Test // mohan add 2025-05-11 std::vector ik2iktot = {0,1}; int nkstot = 2; - bool out_app_flag = true; + int out_app_flag = 1; void SetUp() override { From 5adf04bbef598c1662a80f23ea43c5591a83cb0d Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 15:10:43 +0800 Subject: [PATCH 05/30] chore: remove accidentally committed quality report file --- code_quality_score.txt | 66 ------------------------------------------ 1 file changed, 66 deletions(-) delete mode 100644 code_quality_score.txt diff --git a/code_quality_score.txt b/code_quality_score.txt deleted file mode 100644 index 9a72ceadb64..00000000000 --- a/code_quality_score.txt +++ /dev/null @@ -1,66 +0,0 @@ -====================================================================== -Code Quality Score Report -====================================================================== - -Per-module rollup (sorted by average score, worst first): - Module Files Avg Pass ----------------------------------------------------------------------- - source/source_io 4 55.0 1/4 - - Score File ----------------------------------------------------------------------- - 36 source/source_io/module_parameter/read_input.cpp - 54 source/source_io/test/write_wfc_nao_test.cpp - 58 source/source_io/module_dos/unittests/test_cal_dos.cpp - 72 source/source_io/module_dos/unittests/test_write_dos_pw.cpp - ----------------------------------------------------------------------- -File: source/source_io/module_parameter/read_input.cpp (score: 36) ----------------------------------------------------------------------- - [file_too_long] file: file has 755 lines (exceeds 500 by 255) (-12) - [uppercase_constant] file: 2 occurrence(s) (-2) - [global_dependency] file: 3 occurrence(s) (-9) - [auto_keyword] file: 13 occurrence(s) (capped at 10) (-10) - [high_cyclomatic_complexity] line 99: function 'filter_nonascii_and_comment' has cyclomatic complexity 11 (exceeds 10 by 1) (-1) - [high_cyclomatic_complexity] line 270: function 'create_directory' has cyclomatic complexity 17 (exceeds 10 by 7) (-7) - [high_cyclomatic_complexity] line 353: function 'read_txt_input' has cyclomatic complexity 17 (exceeds 10 by 7) (-7) - [high_cyclomatic_complexity] line 555: function 'write_txt_input' has cyclomatic complexity 21 (exceeds 10 by 11) (-11) - [high_cyclomatic_complexity] line 651: function 'check_ntype' has cyclomatic complexity 15 (exceeds 10 by 5) (-5) - ----------------------------------------------------------------------- -File: source/source_io/test/write_wfc_nao_test.cpp (score: 54) ----------------------------------------------------------------------- - [file_too_long] file: file has 586 lines (exceeds 500 by 86) (-4) - [tab_indentation] file: 21 occurrence(s) (capped at 5) (-5) - [line_too_long] file: 1 occurrence(s) (-1) - [uppercase_constant] file: 67 occurrence(s) (capped at 5) (-5) - [global_dependency] file: 13 occurrence(s) (capped at 10) (-30) - [member_function_too_long] line 157: member function in class WriteWfcLcaoTest spans 57 lines (exceeds 50) (-1) - ----------------------------------------------------------------------- -File: source/source_io/module_dos/unittests/test_cal_dos.cpp (score: 58) ----------------------------------------------------------------------- - [tab_indentation] file: 112 occurrence(s) (capped at 5) (-5) - [line_too_long] file: 3 occurrence(s) (-3) - [uppercase_constant] file: 28 occurrence(s) (capped at 5) (-5) - [global_dependency] file: 9 occurrence(s) (-27) - [duplicate_doc_block] file: 2 occurrence(s) (-2) - ----------------------------------------------------------------------- -File: source/source_io/module_dos/unittests/test_write_dos_pw.cpp (score: 72) ----------------------------------------------------------------------- - [tab_indentation] file: 114 occurrence(s) (capped at 5) (-5) - [line_too_long] file: 2 occurrence(s) (-2) - [uppercase_constant] file: 25 occurrence(s) (capped at 5) (-5) - [global_dependency] file: 4 occurrence(s) (-12) - [unpaired_new_delete] file: 1 occurrence(s) (-1) - [raw_new_keyword] file: 1 occurrence(s) (-1) - [duplicate_doc_block] file: 1 occurrence(s) (-1) - [member_local_name_conflict] line 35: local variable 'kv' in class DosPWTest shadows a member variable (-1) - -====================================================================== -Files scanned: 4 -Files shown: 4 -Average score: 55.0 -Passing (>= 60): 1/4 -====================================================================== From e734403187f88fc0c47defa1adc6c45a1a8659fb Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 15:15:17 +0800 Subject: [PATCH 06/30] fix(io): use local cached directory variables in ctrl_output_fp Replace repeated PARAM.globalv.global_*_dir accesses with the local const variables already declared at function top, eliminating the net +4 PARAM global dependency introduced by the path routing change. --- source/source_io/module_ctrl/ctrl_output_fp.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 7fe1a6e0aad..8e673e4fb4b 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -65,7 +65,7 @@ void ctrl_output_fp(UnitCell& ucell, { for (int is = 0; is < nspin; ++is) { - std::string fn = PARAM.globalv.global_chg_dir + "chg"; + std::string fn = global_chg_dir + "chg"; std::string spin_block; if (nspin == 2 || nspin == 4) @@ -98,7 +98,7 @@ void ctrl_output_fp(UnitCell& ucell, if (XC_Functional::get_ked_flag()) { - fn = PARAM.globalv.global_chg_dir + "tau"; + fn = global_chg_dir + "tau"; fn += spin_block + geom_block + ".cube"; @@ -127,7 +127,7 @@ void ctrl_output_fp(UnitCell& ucell, { for (int is = 0; is < nspin; is++) { - std::string fn = PARAM.globalv.global_pot_dir + "pot"; + std::string fn = global_pot_dir + "pot"; std::string spin_block; if (nspin == 2 || nspin == 4) @@ -161,7 +161,7 @@ void ctrl_output_fp(UnitCell& ucell, } else if (inp.out_pot[0] == 2 && should_output) { - std::string fn = PARAM.globalv.global_pot_dir + "potes"; + std::string fn = global_pot_dir + "potes"; fn += geom_block + ".cube"; const std::vector* pcc_potential = pelec->pot->pcc_electron_potential(); @@ -193,7 +193,7 @@ void ctrl_output_fp(UnitCell& ucell, module_charge::cal_rhog_symm(is, chr, pw_rhod, ucell.symm); } - std::string out_dir = PARAM.globalv.global_elf_dir; + std::string out_dir = global_elf_dir; ModuleIO::write_elf(out_dir, istep, nspin, From 9254e95d8d69dce074e253357d0d60ca49ce2d76 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 15:57:58 +0800 Subject: [PATCH 07/30] test(integrate): fix WFC file path in props_cube.sh for out_app_flag=0 With out_app_flag changed from bool to int, the legacy WFC/ subdirectory is no longer created when out_app_flag=0 (now maps to global_out_dir). Update the multi-k wfc reference path accordingly. --- tests/integrate/validation_tools/props_cube.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrate/validation_tools/props_cube.sh b/tests/integrate/validation_tools/props_cube.sh index 1dfd83270ff..2b1b78526b5 100644 --- a/tests/integrate/validation_tools/props_cube.sh +++ b/tests/integrate/validation_tools/props_cube.sh @@ -154,7 +154,7 @@ if ! test -z "$has_lowf" && [ "$has_lowf" == 1 ]; then else # multi-k point case if ! test -z "$out_app_flag" && [ $out_app_flag == 0 ]; then wfc_name=wfk1g3_nao - input_file=WFC/wfk1g3_nao + input_file=wfk1g3_nao else wfc_name=wfk2_nao input_file=wfk2_nao From 00ece1556d44e42fcdb0b33d61a4ebe7057f62fb Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 16 Oct 2026 16:07:23 +0800 Subject: [PATCH 08/30] feat(io): create subdirs on demand and route dmk/lx/rr/tr to MAT - read_input.cpp: only create PSI/CHG/POT/ELF/DOS/DIP/MAT subdirectories when the corresponding output flag is enabled (out_app_flag=2) - ctrl_scf_lcao.cpp: route write_dmk and angular momentum calculator output to global_mat_dir instead of global_out_dir - hsr_legacy.cpp: route tr_nao.csr to global_matrix_dir - pos_op_writer.cpp: route rr_nao.txt to global_matrix_dir --- .../17_relax/04_relax_with_output_lcao/INPUT | 3 ++- examples/18_md/02_lcao_output/INPUT | 2 ++ .../source_io/module_ctrl/ctrl_scf_lcao.cpp | 5 ++-- source/source_io/module_hs/hsr_legacy.cpp | 2 +- source/source_io/module_hs/pos_op_writer.cpp | 2 +- .../source_io/module_parameter/read_input.cpp | 27 +++++++++++-------- 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/examples/17_relax/04_relax_with_output_lcao/INPUT b/examples/17_relax/04_relax_with_output_lcao/INPUT index 05125b6fa1e..155b3ecc32c 100644 --- a/examples/17_relax/04_relax_with_output_lcao/INPUT +++ b/examples/17_relax/04_relax_with_output_lcao/INPUT @@ -34,7 +34,6 @@ out_dmr 1 out_dos 1 out_band 1 out_stru 1 -out_app_flag 0 out_mat_l 1 out_mat_r 1 @@ -42,3 +41,5 @@ out_mat_t 1 #out_mat_dh 1 out_freq_ion 2 + +out_app_flag 2 diff --git a/examples/18_md/02_lcao_output/INPUT b/examples/18_md/02_lcao_output/INPUT index 7c29d6e1355..c8333b2f0e8 100644 --- a/examples/18_md/02_lcao_output/INPUT +++ b/examples/18_md/02_lcao_output/INPUT @@ -36,3 +36,5 @@ md_tchain 1 dft_functional hse restart_save 1 + +out_app_flag 0 diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index 0be22ccc0dc..fd75ea67da3 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -140,6 +140,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, const bool gamma_only = PARAM.globalv.gamma_only_local; const int nspin = inp.nspin; const std::string global_out_dir = PARAM.globalv.global_out_dir; + const std::string global_mat_dir = PARAM.globalv.global_mat_dir; //------------------------------------------------------------------ //! 1) print out density of states (DOS) @@ -195,7 +196,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, } const int precision = inp.out_dmk[1]; - ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_out_dir, istep, ofs_running); + ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, ofs_running); } //------------------------------------------------------------------ @@ -558,7 +559,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, PARAM.globalv.gamma_only_local, &ofs_running, GlobalV::MY_RANK); - mylcalculator.calculate(inp.suffix, global_out_dir, ucell, inp.out_mat_l[1], GlobalV::MY_RANK, istep_in); + mylcalculator.calculate(inp.suffix, global_mat_dir, ucell, inp.out_mat_l[1], GlobalV::MY_RANK, istep_in); } //------------------------------------------------------------------ diff --git a/source/source_io/module_hs/hsr_legacy.cpp b/source/source_io/module_hs/hsr_legacy.cpp index d3c7d338d85..5503bba8155 100644 --- a/source/source_io/module_hs/hsr_legacy.cpp +++ b/source/source_io/module_hs/hsr_legacy.cpp @@ -184,7 +184,7 @@ void ModuleIO::output_TR(const int istep, } else { - sst << options.global_out_dir << TR_filename; + sst << options.global_matrix_dir << TR_filename; } ofs_running << " Write T(R) matrix in NAO basis to file: " << sst.str() << std::endl; sparse_format::cal_TR(ucell, pv, HS_Arrays, grid, two_center_bundle, orb, options.sparse_threshold); diff --git a/source/source_io/module_hs/pos_op_writer.cpp b/source/source_io/module_hs/pos_op_writer.cpp index f131047041b..e1dd5a2a7ef 100644 --- a/source/source_io/module_hs/pos_op_writer.cpp +++ b/source/source_io/module_hs/pos_op_writer.cpp @@ -223,7 +223,7 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, } else { - ssr << global_out_dir << "rr_nao.txt"; + ssr << global_matrix_dir << "rr_nao.txt"; } ofs_tem1.close(); diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index 1eca7302fec..cc9bf5ba577 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -308,21 +308,26 @@ void ReadInput::create_directory(const Parameter& param) param.input.of_ml_gene_data, param.input.deepks_out_freq_elec > 0); // xiaohui add 2013-09-01 - // Create unified subdirectories when out_app_flag == 2 + // Create unified subdirectories when out_app_flag == 2, only for outputs the user actually requested if (param.input.out_app_flag == 2) { - const std::vector subdirs = { - param.globalv.global_psi_dir, - param.globalv.global_chg_dir, - param.globalv.global_pot_dir, - param.globalv.global_elf_dir, - param.globalv.global_dos_dir, - param.globalv.global_dip_dir, - param.globalv.global_mat_dir + const std::vector> subdir_flags = { + {param.input.out_wfc_lcao > 0 || param.input.out_wfc_pw > 0, param.globalv.global_psi_dir}, + {param.input.out_chg[0] > 0, param.globalv.global_chg_dir}, + {param.input.out_pot[0] > 0 || param.input.out_elf[0] > 0, param.globalv.global_pot_dir}, + {param.input.out_elf[0] > 0, param.globalv.global_elf_dir}, + {param.input.out_dos > 0 || !param.input.out_band.empty() && param.input.out_band[0] > 0, param.globalv.global_dos_dir}, + {param.input.out_dipole, param.globalv.global_dip_dir}, + {param.input.out_dmk[0] > 0 || param.input.out_hsk[0] > 0 || param.input.out_mat_hs2[0] > 0 + || param.input.out_mat_r[0] > 0 || param.input.out_mat_t[0] > 0 || param.input.out_mat_tk[0] > 0 + || param.input.out_mat_xc || param.input.out_mat_xc2[0] > 0 || param.input.out_hr_npz + || param.input.out_dm_npz, param.globalv.global_mat_dir} }; - for (const auto& dir : subdirs) + for (const auto& flag_dir : subdir_flags) { - if (!dir.empty() && dir != param.globalv.global_out_dir) + const bool need_create = flag_dir.first; + const std::string& dir = flag_dir.second; + if (need_create && !dir.empty() && dir != param.globalv.global_out_dir) { int ret = ModuleBase::make_directory(dir); if (ret != 0 && errno != EEXIST) From a77e356a4a48d8fd79b329dc80335418910c8790 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sun, 18 Oct 2026 08:10:35 +0800 Subject: [PATCH 09/30] feat(io): route dmr/tr/rr/dh/ds/lx outputs to MAT directory - Remove md_no_append ternary in hsr_legacy.cpp, pos_op_writer.cpp, hterm_writer.cpp, dhs_sparse_writer.cpp: use global_matrix_dir unconditionally (it already equals global_out_dir when out_app_flag != 2, so no behavior change for legacy modes) - write_dmr: add dmr_dir parameter, route DM(R) to MAT instead of hardcoded global_out_dir; drop parameter.h include - read_input.cpp: add out_dmr/out_mat_l/out_mat_dh/out_mat_ds to MAT subdirectory creation flags Co-Authored-By: Claude Sonnet 4.6 --- source/source_io/module_ctrl/ctrl_scf_lcao.cpp | 5 +++-- source/source_io/module_dm/write_dmr.cpp | 5 +++-- source/source_io/module_dm/write_dmr.h | 2 ++ source/source_io/module_hs/dhs_sparse_writer.cpp | 6 ++---- source/source_io/module_hs/dhs_sparse_writer.h | 1 - source/source_io/module_hs/hsr_legacy.cpp | 7 +++---- source/source_io/module_hs/hterm_writer.cpp | 13 ++++--------- source/source_io/module_hs/pos_op_writer.cpp | 3 +-- .../module_hs/unittests/test_dhs_sparse_writer.cpp | 6 +++--- source/source_io/module_parameter/read_input.cpp | 6 ++++-- 10 files changed, 25 insertions(+), 29 deletions(-) diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index fd75ea67da3..12f9067348f 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -179,9 +179,10 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, if (inp.out_dmr[0]) { const int precision = inp.out_dmr[1]; + const bool append_dmr = (out_app_flag == 1); - ModuleIO::write_dmr(dm->get_dmr_vec(), &ucell, precision, pv, (out_app_flag == 1), - ucell.get_iat2iwt(), ucell.nat, istep, ofs_running); + ModuleIO::write_dmr(dm->get_dmr_vec(), &ucell, precision, pv, append_dmr, + ucell.get_iat2iwt(), ucell.nat, global_mat_dir, istep, ofs_running); } //------------------------------------------------------------------ diff --git a/source/source_io/module_dm/write_dmr.cpp b/source/source_io/module_dm/write_dmr.cpp index 6b95e726a31..58bdd2ec16a 100644 --- a/source/source_io/module_dm/write_dmr.cpp +++ b/source/source_io/module_dm/write_dmr.cpp @@ -1,7 +1,7 @@ #include "write_dmr.h" #include "source_io/module_output/spin_tag.h" -#include "source_io/module_parameter/parameter.h" +#include "source_base/global_variable.h" #include "source_hamilt/module_hcontainer/hcontainer_funcs.h" #include "source_hamilt/module_hcontainer/output_hcontainer.h" #include "source_cell/ucell_io.h" @@ -85,6 +85,7 @@ void write_dmr(const std::vector*> dmr, const bool append, const int* iat2iwt, const int nat, + const std::string& dmr_dir, const int istep, std::ofstream& ofs_running) { @@ -110,7 +111,7 @@ void write_dmr(const std::vector*> dmr, // out_type = 1, csr format; // out_type = 2, npz format (currently not support) const int out_type = 1; - std::string fname = PARAM.globalv.global_out_dir + dmr_gen_fname(out_type, ispin, append, istep); + std::string fname = dmr_dir + dmr_gen_fname(out_type, ispin, append, istep); write_dmr_csr(fname, ucell, precision, &dm_serial, istep, ispin, nspin); const std::string spin_tag = ModuleIO::make_spin_tag(ispin, nspin); ofs_running << " Write DM(R)" << spin_tag << " matrix in NAO basis to file: " << fname << std::endl; diff --git a/source/source_io/module_dm/write_dmr.h b/source/source_io/module_dm/write_dmr.h index 644e2828b52..957553242e8 100644 --- a/source/source_io/module_dm/write_dmr.h +++ b/source/source_io/module_dm/write_dmr.h @@ -54,6 +54,7 @@ void write_dmr_csr(std::string& fname, * @param ispin The spin index, starting from 0. * @param append Whether to append the data to an existing file or create a new file. The file name is related to this * flag. + * @param dmr_dir The directory the DM(R) files are written into. * @param istep The ION step, starting from 0. */ void write_dmr(const std::vector*> dmr, @@ -63,6 +64,7 @@ void write_dmr(const std::vector*> dmr, const bool append, const int* iat2iwt, const int nat, + const std::string& dmr_dir, const int istep, std::ofstream& ofs_running); } // namespace ModuleIO diff --git a/source/source_io/module_hs/dhs_sparse_writer.cpp b/source/source_io/module_hs/dhs_sparse_writer.cpp index 4d69c718a93..557adfa59e7 100644 --- a/source/source_io/module_hs/dhs_sparse_writer.cpp +++ b/source/source_io/module_hs/dhs_sparse_writer.cpp @@ -21,7 +21,6 @@ void ModuleIO::save_dH_sparse(const int& istep, const bool& binary, const std::string& fileflag, const int precision, - const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, const int out_app_flag, @@ -112,20 +111,19 @@ void ModuleIO::save_dH_sparse(const int& istep, } } - const bool md_no_append = (calculation == "md") && (out_app_flag != 1); for (auto& comp: comps) { for (int ispin = 0; ispin < 2; ++ispin) { if (istep >= 0) { - comp.fname[ispin] << (md_no_append ? global_matrix_dir : global_out_dir) + comp.fname[ispin] << global_matrix_dir << "d" << fileflag << "r" << comp.axis << "s" << (ispin + 1) << "g" << (istep + 1) << "_nao.csr"; } else { - comp.fname[ispin] << global_out_dir + comp.fname[ispin] << global_matrix_dir << "d" << fileflag << "r" << comp.axis << "s" << (ispin + 1) << "_nao.csr"; } diff --git a/source/source_io/module_hs/dhs_sparse_writer.h b/source/source_io/module_hs/dhs_sparse_writer.h index 7d6337d577e..59677d8f6ce 100644 --- a/source/source_io/module_hs/dhs_sparse_writer.h +++ b/source/source_io/module_hs/dhs_sparse_writer.h @@ -17,7 +17,6 @@ void save_dH_sparse(const int& istep, const bool& binary, const std::string& fileflag, const int precision, - const std::string& global_out_dir, const std::string& global_matrix_dir, const std::string& calculation, const int out_app_flag, diff --git a/source/source_io/module_hs/hsr_legacy.cpp b/source/source_io/module_hs/hsr_legacy.cpp index 5503bba8155..8b3fc5a4dfc 100644 --- a/source/source_io/module_hs/hsr_legacy.cpp +++ b/source/source_io/module_hs/hsr_legacy.cpp @@ -40,7 +40,7 @@ void ModuleIO::output_dSR(const int& istep, // mohan update 2024-04-01 const std::string fileflag_s = "s"; ModuleIO::save_dH_sparse(istep, pv, HS_Arrays, options.sparse_threshold, options.binary, - fileflag_s, options.precision, options.global_out_dir, + fileflag_s, options.precision, options.global_matrix_dir, options.calculation, options.out_app_flag, options.nspin, nlocal, ofs_running); @@ -86,7 +86,7 @@ void ModuleIO::output_dHR(const int& istep, // mohan update 2024-04-01 const std::string fileflag_h = "h"; ModuleIO::save_dH_sparse(istep, pv, HS_Arrays, options.sparse_threshold, options.binary, - fileflag_h, options.precision, options.global_out_dir, + fileflag_h, options.precision, options.global_matrix_dir, options.calculation, options.out_app_flag, options.nspin, nlocal, ofs_running); @@ -174,13 +174,12 @@ void ModuleIO::output_TR(const int istep, ModuleBase::timer::start("ModuleIO", "output_TR"); std::stringstream sst; - const bool md_no_append = (options.calculation == "md") && (options.out_app_flag != 1); if (istep >= 0) { // insert the 1-indexed ionic-step suffix right after the "tr" prefix, // i.e. tr_nao.csr -> trg{step+1}_nao.csr (aligned with lxg/rrg naming) const std::string base = TR_filename.substr(0, TR_filename.size() - std::string("_nao.csr").size()); - sst << (md_no_append ? options.global_matrix_dir : options.global_out_dir) << base << "g" << (istep + 1) << "_nao.csr"; + sst << options.global_matrix_dir << base << "g" << (istep + 1) << "_nao.csr"; } else { diff --git a/source/source_io/module_hs/hterm_writer.cpp b/source/source_io/module_hs/hterm_writer.cpp index ee6c997c7b3..95910677d11 100644 --- a/source/source_io/module_hs/hterm_writer.cpp +++ b/source/source_io/module_hs/hterm_writer.cpp @@ -78,9 +78,6 @@ static void gather_and_write(const std::string& prefix, const bool append, const int* iat2iwt, const int nat, - const std::string& calculation, - const int out_app_flag, - const std::string& global_out_dir, const std::string& global_matrix_dir, std::ofstream* ofs_running) { @@ -94,9 +91,7 @@ static void gather_and_write(const std::string& prefix, if (GlobalV::MY_RANK == 0) #endif { - const bool md_no_append = (calculation == "md") && (out_app_flag != 1); - const std::string& out_dir = md_no_append ? global_matrix_dir : global_out_dir; - const std::string fname = out_dir + hsr_gen_fname(prefix, ispin, append, istep); + const std::string fname = global_matrix_dir + hsr_gen_fname(prefix, ispin, append, istep); // H(R) component terms (T, V^NL, V^L, V^H, V^XC, V^EXX) are parts of // the Hamiltonian, not the full H(R): they do not carry the Fermi annotation const double no_efermi = 0.0; @@ -215,8 +210,8 @@ static void write_h_term(WriteHParams& params, const HTermSpec& spec) if (also_hR) { gather_and_write(spec.r_prefix, spec.r_label, spec.term_name, hR_tmp, ucell, pv, nspin, ispin, - istep, append, iat2iwt, nat, params.calculation, params.out_app_flag, - params.global_out_dir, params.global_matrix_dir, params.ofs_running); + istep, append, iat2iwt, nat, + params.global_matrix_dir, params.ofs_running); } } @@ -402,7 +397,7 @@ static void write_h_exx_impl(const UnitCell& ucell, if (also_hR) { gather_and_write("vexx", "V^EXX", "exx", hR_tmp, ucell, pv, nspin, ispin, istep, append, - iat2iwt, nat, calculation, out_app_flag, global_out_dir, global_matrix_dir, + iat2iwt, nat, global_matrix_dir, ofs_running); } } diff --git a/source/source_io/module_hs/pos_op_writer.cpp b/source/source_io/module_hs/pos_op_writer.cpp index e1dd5a2a7ef..8f75d52ed72 100644 --- a/source/source_io/module_hs/pos_op_writer.cpp +++ b/source/source_io/module_hs/pos_op_writer.cpp @@ -216,10 +216,9 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, if (GlobalV::DRANK == 0) { std::stringstream ssr; - const bool md_no_append = (calculation == "md") && (out_app_flag != 1); if (step >= 0) { - ssr << (md_no_append ? global_matrix_dir : global_out_dir) << "rrg" << (step + 1) << "_nao.txt"; + ssr << global_matrix_dir << "rrg" << (step + 1) << "_nao.txt"; } else { diff --git a/source/source_io/module_hs/unittests/test_dhs_sparse_writer.cpp b/source/source_io/module_hs/unittests/test_dhs_sparse_writer.cpp index 3885c7c9472..c667523e075 100644 --- a/source/source_io/module_hs/unittests/test_dhs_sparse_writer.cpp +++ b/source/source_io/module_hs/unittests/test_dhs_sparse_writer.cpp @@ -40,7 +40,7 @@ TEST(DhsSparseWriter, TextCountsOnlyValuesAboveThreshold) arrays.dHRx_sparse[0][r_vector][1][0] = 0.0; arrays.dHRx_sparse[0][r_vector][1][1] = -2.0; - ModuleIO::save_dH_sparse(5, pv, arrays, 1e-10, false, "h", 8, "./", "./", "scf", false, 1, 2, ofs_running); + ModuleIO::save_dH_sparse(5, pv, arrays, 1e-10, false, "h", 8, "./", "scf", false, 1, 2, ofs_running); const std::vector lines = read_lines("dhrxs1g6_nao.csr"); ASSERT_GE(lines.size(), 7); @@ -91,7 +91,7 @@ TEST(DhsSparseWriter, BinaryCountsOnlyValuesAboveThreshold) arrays.dHRx_sparse[0][r_vector][1][0] = 0.0; arrays.dHRx_sparse[0][r_vector][1][1] = -2.0; - ModuleIO::save_dH_sparse(6, pv, arrays, 1e-10, true, "h", 8, "./", "./", "scf", false, 1, 2, ofs_running); + ModuleIO::save_dH_sparse(6, pv, arrays, 1e-10, true, "h", 8, "./", "scf", false, 1, 2, ofs_running); std::ifstream ifs("dhrxs1g7_nao.csr", std::ios::binary); ASSERT_TRUE(ifs.is_open()); @@ -127,7 +127,7 @@ TEST(DhsSparseWriter, SocWritesAllDirections) arrays.dHRy_soc_sparse[r_vector][0][1] = std::complex(2.0, -1.0); arrays.dHRz_soc_sparse[r_vector][1][1] = std::complex(-3.0, 0.5); - ModuleIO::save_dH_sparse(7, pv, arrays, 1e-10, false, "s", 8, "./", "./", "scf", false, 4, 2, ofs_running); + ModuleIO::save_dH_sparse(7, pv, arrays, 1e-10, false, "s", 8, "./", "scf", false, 4, 2, ofs_running); const std::string x_output = read_file("dsrxs1g8_nao.csr"); const std::string y_output = read_file("dsrys1g8_nao.csr"); diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index cc9bf5ba577..aec29ce0752 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -318,8 +318,10 @@ void ReadInput::create_directory(const Parameter& param) {param.input.out_elf[0] > 0, param.globalv.global_elf_dir}, {param.input.out_dos > 0 || !param.input.out_band.empty() && param.input.out_band[0] > 0, param.globalv.global_dos_dir}, {param.input.out_dipole, param.globalv.global_dip_dir}, - {param.input.out_dmk[0] > 0 || param.input.out_hsk[0] > 0 || param.input.out_mat_hs2[0] > 0 - || param.input.out_mat_r[0] > 0 || param.input.out_mat_t[0] > 0 || param.input.out_mat_tk[0] > 0 + {param.input.out_dmk[0] > 0 || param.input.out_dmr[0] > 0 || param.input.out_hsk[0] > 0 + || param.input.out_mat_hs2[0] > 0 || param.input.out_mat_r[0] > 0 || param.input.out_mat_t[0] > 0 + || param.input.out_mat_tk[0] > 0 || param.input.out_mat_l[0] > 0 + || param.input.out_mat_dh[0] > 0 || param.input.out_mat_ds[0] > 0 || param.input.out_mat_xc || param.input.out_mat_xc2[0] > 0 || param.input.out_hr_npz || param.input.out_dm_npz, param.globalv.global_mat_dir} }; From 3a384854cfbe9b506fc7959e9093c162e21051e6 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sun, 18 Oct 2026 08:50:06 +0800 Subject: [PATCH 10/30] feat(io): unify STRU output directory and naming for relax and MD - global_stru_dir now follows out_app_flag: STRU/ for flag 2, global_out_dir otherwise (consistent with MAT/PSI/CHG pattern) - relax_driver: route STRU_NOW/STRU{step}/STRU_FINAL through global_stru_dir instead of global_out_dir - run_md: drop _MD suffix, use STRU{step} naming consistent with relax - global_file: remove unconditional STRU dir creation from make_dir_out; remove global_stru_dir parameter - read_input: create STRU dir on demand (out_stru>0 or MD) Co-Authored-By: Claude Sonnet 4.6 --- source/source_base/global_file.cpp | 40 ------------------- source/source_base/global_file.h | 1 - source/source_base/test/global_file_test.cpp | 20 +--------- .../source_io/module_parameter/read_input.cpp | 2 +- .../module_parameter/read_set_globalv.cpp | 14 +------ .../source_io/test_serial/io_sys_var_test.cpp | 3 +- .../source_io/test_serial/read_input_test.cpp | 1 - source/source_md/run_md.cpp | 2 +- source/source_relax/relax_driver.cpp | 2 +- 9 files changed, 9 insertions(+), 76 deletions(-) diff --git a/source/source_base/global_file.cpp b/source/source_base/global_file.cpp index 76a16036be7..1889357c2c8 100644 --- a/source/source_base/global_file.cpp +++ b/source/source_base/global_file.cpp @@ -30,7 +30,6 @@ void ModuleBase::Global_File::make_dir_out( const bool &restart, const bool out_alllog, const std::string &global_out_dir, - const std::string &global_stru_dir, const std::string &global_matrix_dir, const std::string &global_wfc_dir, const std::string &global_mlkedf_descriptor_dir, @@ -84,45 +83,6 @@ void ModuleBase::Global_File::make_dir_out( MPI_Barrier(MPI_COMM_WORLD); #endif - if(calculation == "md") - { - int make_dir_stru = 0; - - times = 0; - while(times0) { break; -} - ++times; - } - -#ifdef __MPI - if(make_dir_stru==0) - { - std::cout << " CAN NOT MAKE THE STRU DIR......." << std::endl; - ModuleBase::QUIT(); - } - MPI_Barrier(MPI_COMM_WORLD); -#endif - } - // make dir for HS matrix output in md calculation if((out_dir) && calculation == "md") { diff --git a/source/source_base/global_file.h b/source/source_base/global_file.h index d051b5e81c4..4ce5adb18d3 100644 --- a/source/source_base/global_file.h +++ b/source/source_base/global_file.h @@ -28,7 +28,6 @@ namespace Global_File const bool &restart, const bool out_alllog, const std::string &global_out_dir, - const std::string &global_stru_dir, const std::string &global_matrix_dir, const std::string &global_wfc_dir, const std::string &global_mlkedf_descriptor_dir, diff --git a/source/source_base/test/global_file_test.cpp b/source/source_base/test/global_file_test.cpp index 07184237553..fe7bdac7446 100644 --- a/source/source_base/test/global_file_test.cpp +++ b/source/source_base/test/global_file_test.cpp @@ -25,7 +25,6 @@ class GlobalFile : public testing::Test { protected: std::string global_out_dir; - std::string global_stru_dir; std::string global_matrix_dir; std::string global_wfc_dir; std::string global_mlkedf_descriptor_dir; @@ -34,7 +33,6 @@ class GlobalFile : public testing::Test { const std::string suffix = "Si"; global_out_dir = "OUT." + suffix + "/"; - global_stru_dir = global_out_dir + "STRU/"; global_matrix_dir = global_out_dir + "matrix/"; global_wfc_dir = global_out_dir + "WFC/"; global_mlkedf_descriptor_dir = global_out_dir + "MLKEDF_Descriptors/"; @@ -47,7 +45,7 @@ TEST_F(GlobalFile,mkdirout) std::string output; testing::internal::CaptureStdout(); ModuleBase::Global_File::make_dir_out("Si","m",false,false,0,true,true, - global_out_dir, global_stru_dir, global_matrix_dir, global_wfc_dir, + global_out_dir, global_matrix_dir, global_wfc_dir, global_mlkedf_descriptor_dir, global_deepks_label_elec_dir, "running_m_1.log", false, false); output = testing::internal::GetCapturedStdout(); @@ -58,22 +56,9 @@ TEST_F(GlobalFile,mkdirout) std::string dd = "OUT.Si/running_m_1.log"; remove(dd.c_str()); - testing::internal::CaptureStdout(); - ModuleBase::Global_File::make_dir_out("Si","md",false,false,0,true,false, - global_out_dir, global_stru_dir, global_matrix_dir, global_wfc_dir, - global_mlkedf_descriptor_dir, global_deepks_label_elec_dir, - "running_md.log", false, false); - output = testing::internal::GetCapturedStdout(); - EXPECT_THAT(output,testing::HasSubstr("MAKE THE STRU DIR")); - EXPECT_TRUE(GlobalV::ofs_running.is_open()); - GlobalV::ofs_running.close(); - GlobalV::ofs_warning.close(); - std::string bb = "OUT.Si/running_md.log"; - remove(bb.c_str()); - testing::internal::CaptureStdout(); ModuleBase::Global_File::make_dir_out("Si","md",true,false,0,true,true, - global_out_dir, global_stru_dir, global_matrix_dir, global_wfc_dir, + global_out_dir, global_matrix_dir, global_wfc_dir, global_mlkedf_descriptor_dir, global_deepks_label_elec_dir, "running_md_1.log", false, false); output = testing::internal::GetCapturedStdout(); @@ -84,7 +69,6 @@ TEST_F(GlobalFile,mkdirout) remove(cc.c_str()); std::string aa = "OUT.Si/warning.log"; remove(aa.c_str()); - rmdir(global_stru_dir.c_str()); rmdir(global_matrix_dir.c_str()); rmdir(global_out_dir.c_str()); } diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index aec29ce0752..aa89d49dd33 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -299,7 +299,6 @@ void ReadInput::create_directory(const Parameter& param) param.input.mdp.md_restart, param.input.out_alllog, param.globalv.global_out_dir, - param.globalv.global_stru_dir, param.globalv.global_matrix_dir, param.globalv.global_wfc_dir, param.globalv.global_mlkedf_descriptor_dir, @@ -312,6 +311,7 @@ void ReadInput::create_directory(const Parameter& param) if (param.input.out_app_flag == 2) { const std::vector> subdir_flags = { + {param.input.out_stru > 0 || param.input.calculation == "md", param.globalv.global_stru_dir}, {param.input.out_wfc_lcao > 0 || param.input.out_wfc_pw > 0, param.globalv.global_psi_dir}, {param.input.out_chg[0] > 0, param.globalv.global_chg_dir}, {param.input.out_pot[0] > 0 || param.input.out_elf[0] > 0, param.globalv.global_pot_dir}, diff --git a/source/source_io/module_parameter/read_set_globalv.cpp b/source/source_io/module_parameter/read_set_globalv.cpp index 94bfaeb06ac..3a3ed49d1ef 100644 --- a/source/source_io/module_parameter/read_set_globalv.cpp +++ b/source/source_io/module_parameter/read_set_globalv.cpp @@ -85,23 +85,12 @@ void ReadInput::set_global_dir(const Input_para& inp, System_para& sys) sys.global_out_dir = prefix + inp.suffix + "/"; sys.global_out_dir = to_dir(sys.global_out_dir); - /// get the global output directory - sys.global_stru_dir = sys.global_out_dir + "STRU/"; - sys.global_stru_dir = to_dir(sys.global_stru_dir); - - /// get the global output directory - sys.global_matrix_dir = sys.global_out_dir + "matrix/"; - sys.global_matrix_dir = to_dir(sys.global_matrix_dir); - - /// get the global output directory - sys.global_wfc_dir = sys.global_out_dir + "WFC/"; - sys.global_wfc_dir = to_dir(sys.global_wfc_dir); - /// Set subdirectories based on out_app_flag /// out_app_flag == 2: use unified uppercase subdirectories /// otherwise: keep backward-compatible behavior (all point to global_out_dir) if (inp.out_app_flag == 2) { + sys.global_stru_dir = sys.global_out_dir + "STRU/"; sys.global_psi_dir = sys.global_out_dir + "PSI/"; sys.global_chg_dir = sys.global_out_dir + "CHG/"; sys.global_pot_dir = sys.global_out_dir + "POT/"; @@ -112,6 +101,7 @@ void ReadInput::set_global_dir(const Input_para& inp, System_para& sys) } else { + sys.global_stru_dir = sys.global_out_dir; sys.global_psi_dir = sys.global_out_dir; sys.global_chg_dir = sys.global_out_dir; sys.global_pot_dir = sys.global_out_dir; diff --git a/source/source_io/test_serial/io_sys_var_test.cpp b/source/source_io/test_serial/io_sys_var_test.cpp index 7acb032a557..5156027973b 100644 --- a/source/source_io/test_serial/io_sys_var_test.cpp +++ b/source/source_io/test_serial/io_sys_var_test.cpp @@ -67,7 +67,8 @@ TEST_F(InputTest, Item_test) TestParameters::set_global_dir(readinput, input, sys); EXPECT_EQ(sys.global_out_dir, "OUT.test/"); - EXPECT_EQ(sys.global_stru_dir, "OUT.test/STRU/"); + // out_app_flag defaults to 1, so stru dir maps to global out dir + EXPECT_EQ(sys.global_stru_dir, "OUT.test/"); // out_app_flag defaults to 1, so matrix dir maps to global out dir EXPECT_EQ(sys.global_matrix_dir, "OUT.test/"); EXPECT_EQ(sys.global_wfc_dir, "OUT.test/"); diff --git a/source/source_io/test_serial/read_input_test.cpp b/source/source_io/test_serial/read_input_test.cpp index 236b8e8f87d..b948aa9116c 100644 --- a/source/source_io/test_serial/read_input_test.cpp +++ b/source/source_io/test_serial/read_input_test.cpp @@ -39,7 +39,6 @@ void make_dir_out(const std::string& suffix, const bool& restart, const bool out_alllog, const std::string& global_out_dir, - const std::string& global_stru_dir, const std::string& global_matrix_dir, const std::string& global_wfc_dir, const std::string& global_mlkedf_descriptor_dir, diff --git a/source/source_md/run_md.cpp b/source/source_md/run_md.cpp index 114f9cbb3a5..d87849c8791 100644 --- a/source/source_md/run_md.cpp +++ b/source/source_md/run_md.cpp @@ -288,7 +288,7 @@ void md_line(MDCell& mdcell, mdcell.sync_backing_unitcell(); } std::stringstream file; - file << PARAM.globalv.global_stru_dir << "STRU_MD_" << mdrun->step_ + mdrun->step_rst_; + file << PARAM.globalv.global_stru_dir << "STRU" << mdrun->step_ + mdrun->step_rst_; mdcell::print_stru_file(mdcell, mdcell.stru_meta(), file.str()); mdrun->write_restart(PARAM.globalv.global_out_dir); } diff --git a/source/source_relax/relax_driver.cpp b/source/source_relax/relax_driver.cpp index 7497fc8e1c9..1756d7ebf79 100644 --- a/source/source_relax/relax_driver.cpp +++ b/source/source_relax/relax_driver.cpp @@ -21,7 +21,7 @@ void Relax_Driver::relax_driver( // Cache global configuration once so downstream helpers do not each reach // into PARAM / GlobalV. - out_dir_ = PARAM.globalv.global_out_dir; + out_dir_ = PARAM.globalv.global_stru_dir; deepks_setorb_ = PARAM.globalv.deepks_setorb; my_rank_ = GlobalV::MY_RANK; From aabef5f78bd36b1b1fa79616cdfa48aecb346028 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sun, 18 Oct 2026 09:08:05 +0800 Subject: [PATCH 11/30] feat(md): replace md_restartfreq with out_freq_ion + out_stru - run_md: use out_freq_ion (with md_restartfreq fallback) as the effective ionic-step output frequency for both STRU and restart - run_md: gate STRU output on out_stru > 0, consistent with relax - read_input: simplify STRU dir creation to out_stru > 0 - read_inp_out: update out_stru description to cover MD - read_input_item_md: mark md_restartfreq as deprecated Co-Authored-By: Claude Sonnet 4.6 --- .../module_parameter/read_inp_out.cpp | 2 +- .../source_io/module_parameter/read_input.cpp | 2 +- .../module_parameter/read_input_item_md.cpp | 4 +++- source/source_md/run_md.cpp | 22 ++++++++++++++----- source/source_relax/relax_driver.cpp | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index dd600da3666..5d172868e6d 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -456,7 +456,7 @@ Note: values 2 and 3 are no longer supported. Setting out_dos to 2 or 3 will rai item.annotation = "output the structure files per ion step"; item.category = "Output information"; item.type = "Integer"; - item.description = "Controls the output of structure files per ionic step. The files are written to the OUT.{suffix}/ directory. Each file corresponds to the structure at RELAX STEP ${istep} (for scf/nscf this is the single step), i.e., the structure for which that step's energy was computed (before the relax move), and includes a header comment with the ABACUS version, timestamp, energy, and stress tensor. When out_freq_ion is positive, the numbered files STRU{istep} (or STRU{istep}.cif) are written every out_freq_ion steps during geometry relaxation; when out_freq_ion is 0, no numbered files are output. This parameter is effective for scf/nscf/relax/cell-relax; for scf/nscf only STRU_FINAL (or STRU_FINAL.cif) is written, and structure output is disabled by default unless out_stru is set explicitly. Molecular dynamics structure output is instead controlled by md_restartfreq (STRU_MD_*).\n" + item.description = "Controls the output of structure files per ionic step. The files are written to the OUT.{suffix}/ directory. Each file corresponds to the structure at the current ionic step, and includes a header comment with the ABACUS version, timestamp, energy, and stress tensor. When out_freq_ion is positive, the numbered files STRU{istep} (or STRU{istep}.cif) are written every out_freq_ion steps; when out_freq_ion is 0, no numbered files are output. This parameter is effective for scf/nscf/relax/cell-relax and md. For scf/nscf only STRU_FINAL (or STRU_FINAL.cif) is written. For MD, structure output follows the same out_freq_ion frequency as the restart file.\n" " - 0: No structure files are output.\n" " - 1: ABACUS STRU format files are output. The latest structure is written to STRU_NOW (overwritten each step), the numbered file STRU{istep} (e.g., STRU1, STRU2) is written every out_freq_ion steps (when out_freq_ion is positive), and the final converged structure is written to STRU_FINAL. No CIF files are output.\n" " - 2: CIF format files are output. The latest structure is written to STRU_NOW.cif (overwritten each step), the numbered file STRU{istep}.cif (e.g., STRU1.cif, STRU2.cif) is written every out_freq_ion steps (when out_freq_ion is positive), and the final converged structure is written to STRU_FINAL.cif. No non-CIF files are output.\n" diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index aa89d49dd33..56b4b750146 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -311,7 +311,7 @@ void ReadInput::create_directory(const Parameter& param) if (param.input.out_app_flag == 2) { const std::vector> subdir_flags = { - {param.input.out_stru > 0 || param.input.calculation == "md", param.globalv.global_stru_dir}, + {param.input.out_stru > 0, param.globalv.global_stru_dir}, {param.input.out_wfc_lcao > 0 || param.input.out_wfc_pw > 0, param.globalv.global_psi_dir}, {param.input.out_chg[0] > 0, param.globalv.global_chg_dir}, {param.input.out_pot[0] > 0 || param.input.out_elf[0] > 0, param.globalv.global_pot_dir}, diff --git a/source/source_io/module_parameter/read_input_item_md.cpp b/source/source_io/module_parameter/read_input_item_md.cpp index d5f6dc6bd67..eb6ac3db167 100644 --- a/source/source_io/module_parameter/read_input_item_md.cpp +++ b/source/source_io/module_parameter/read_input_item_md.cpp @@ -169,7 +169,9 @@ Note that md_tlast is only used in NVT/NPT simulations. If md_tlast is unset or item.annotation = "The period to output MD restart information"; item.category = "Molecular dynamics"; item.type = "Integer"; - item.description = "The output frequency of OUT.{suffix}/STRU_MD_*, which are used to restart molecular dynamics calculations, see md_restart in detail. Set to 0 to disable MD restart output."; + item.description = "Deprecated: use out_freq_ion to control MD output frequency instead. " + "This parameter is kept for backward compatibility as a fallback when " + "out_freq_ion is not set (0)."; item.default_value = "5"; item.unit = ""; read_sync_int(input.mdp.md_restartfreq); diff --git a/source/source_md/run_md.cpp b/source/source_md/run_md.cpp index d87849c8791..33a9432797e 100644 --- a/source/source_md/run_md.cpp +++ b/source/source_md/run_md.cpp @@ -188,6 +188,13 @@ void md_line(MDCell& mdcell, apply_plumed_forces(mdcell, plumed_forces); }; + /// Effective ionic-step output frequency. + /// Prefer out_freq_ion; fall back to md_restartfreq for backward + /// compatibility when out_freq_ion is not set. + const int eff_output_freq = (param_in.inp.out_freq_ion > 0) + ? param_in.inp.out_freq_ion + : param_in.mdp.md_restartfreq; + /// md cycle, mohan update 2026-01-04, change '<=' to '<' while ((mdrun->step_ + mdrun->step_rst_) < param_in.mdp.md_nstep && !mdrun->stop) { @@ -280,16 +287,21 @@ void md_line(MDCell& mdcell, mdrun->virial); } - if (param_in.mdp.md_restartfreq > 0 - && (mdrun->step_ + mdrun->step_rst_) % param_in.mdp.md_restartfreq == 0) + const int current_step = mdrun->step_ + mdrun->step_rst_; + const bool do_output_step = eff_output_freq > 0 + && current_step % eff_output_freq == 0; + if (do_output_step) { if (mdcell.has_backing_unitcell()) { mdcell.sync_backing_unitcell(); } - std::stringstream file; - file << PARAM.globalv.global_stru_dir << "STRU" << mdrun->step_ + mdrun->step_rst_; - mdcell::print_stru_file(mdcell, mdcell.stru_meta(), file.str()); + if (param_in.inp.out_stru > 0) + { + std::stringstream file; + file << PARAM.globalv.global_stru_dir << "STRU" << current_step; + mdcell::print_stru_file(mdcell, mdcell.stru_meta(), file.str()); + } mdrun->write_restart(PARAM.globalv.global_out_dir); } diff --git a/source/source_relax/relax_driver.cpp b/source/source_relax/relax_driver.cpp index 1756d7ebf79..3c3d5c9273f 100644 --- a/source/source_relax/relax_driver.cpp +++ b/source/source_relax/relax_driver.cpp @@ -186,7 +186,7 @@ bool Relax_Driver::relax_step(std::vector& steps, void Relax_Driver::stru_out(const int istep, UnitCell& ucell, const Input_para& inp, const double etot, const ModuleBase::matrix& stress, const ModuleBase::matrix& force) { - // out_stru is effective for scf/nscf/relax/cell-relax (md writes STRU_MD_* via md_restartfreq) + // out_stru is effective for scf/nscf/relax/cell-relax; md handles STRU output in run_md.cpp if (inp.calculation != "relax" && inp.calculation != "cell-relax" && inp.calculation != "scf" && inp.calculation != "nscf") { From 443c337ef54f7a1674197965bb878e6c977becb5 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sun, 18 Oct 2026 09:19:13 +0800 Subject: [PATCH 12/30] fix(md): start MD STRU numbering from 1 (like relax) Co-Authored-By: Claude Sonnet 4.6 --- source/source_md/run_md.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/source_md/run_md.cpp b/source/source_md/run_md.cpp index 33a9432797e..1e92c2e9556 100644 --- a/source/source_md/run_md.cpp +++ b/source/source_md/run_md.cpp @@ -298,8 +298,9 @@ void md_line(MDCell& mdcell, } if (param_in.inp.out_stru > 0) { + const int stru_step = current_step + 1; std::stringstream file; - file << PARAM.globalv.global_stru_dir << "STRU" << current_step; + file << PARAM.globalv.global_stru_dir << "STRU" << stru_step; mdcell::print_stru_file(mdcell, mdcell.stru_meta(), file.str()); } mdrun->write_restart(PARAM.globalv.global_out_dir); From b106783a52d54ce39a385bdbd3811ed5f090bb7c Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Mon, 19 Oct 2026 09:17:36 +0800 Subject: [PATCH 13/30] fix(io): restore MD restart STRU lookup after out_app_flag rework - commit 3a384854c made global_stru_dir follow out_app_flag (default 1 maps it to the output dir), but the MD restart lookup still probed STRU_MD_{istep} there, so restart cases whose fixtures live in OUT.suffix/STRU/ (e.g. tests/04_FF/14_LJ_rescale_v) failed to start - search the current global_stru_dir first, then fall back to the legacy OUT.suffix/STRU/ layout; accept both the new STRU{istep+1} naming (1-based, unified with relax) and the legacy STRU_MD_{istep} - quit with a clear diagnostic when no restart structure file exists - add unit test covering both naming schemes in a user read_file_dir --- .../module_parameter/read_set_globalv.cpp | 56 ++++++++++++++++++- .../source_io/test_serial/io_sys_var_test.cpp | 53 ++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/source/source_io/module_parameter/read_set_globalv.cpp b/source/source_io/module_parameter/read_set_globalv.cpp index 3a3ed49d1ef..5a74f8b41b8 100644 --- a/source/source_io/module_parameter/read_set_globalv.cpp +++ b/source/source_io/module_parameter/read_set_globalv.cpp @@ -3,6 +3,35 @@ #include "source_base/global_variable.h" #include "source_base/tool_quit.h" #include "source_io/module_parameter/parameter.h" + +namespace +{ +/// @brief Find the structure file for restarting a molecular dynamics run. +/// @param stru_dir directory where the previous run wrote its structure files +/// @param istep number of completed ionic steps recorded in Restart_md.txt +/// @return path of the first existing structure file, empty if none exists +/// @details Two naming schemes are accepted, in order: +/// - "STRU{istep + 1}": written by run_md since the ionic-step numbering +/// was unified with relax (1-based); +/// - "STRU_MD_{istep}": written by older ABACUS versions. +std::string find_md_restart_stru(const std::string& stru_dir, const int istep) +{ + const std::string stru_new = stru_dir + "STRU" + std::to_string(istep + 1); + std::ifstream file_new(stru_new.c_str()); + if (file_new.good()) + { + return stru_new; + } + const std::string stru_old = stru_dir + "STRU_MD_" + std::to_string(istep); + std::ifstream file_old(stru_old.c_str()); + if (file_old.good()) + { + return stru_old; + } + return ""; +} +} // namespace + namespace ModuleIO { /// @note Here para.inp has been synchronized of all ranks. @@ -131,15 +160,36 @@ void ReadInput::set_global_dir(const Input_para& inp, System_para& sys) /// get the stru file for md restart case if (inp.calculation == "md" && inp.mdp.md_restart) { - int istep = current_md_step(sys.global_readin_dir); + const int istep = current_md_step(sys.global_readin_dir); if (inp.read_file_dir == to_dir("OUT." + inp.suffix)) { - sys.global_in_stru = sys.global_stru_dir + "STRU_MD_" + std::to_string(istep); + // Restart from the default output directory: search the current + // STRU output directory, which follows out_app_flag. + const std::string stru_dir = sys.global_stru_dir; + sys.global_in_stru = find_md_restart_stru(stru_dir, istep); + if (sys.global_in_stru.empty()) + { + // Releases before the out_app_flag rework always wrote + // STRU_MD_{istep} into a fixed STRU/ subdirectory of the + // output directory; keep those outputs restartable. + const std::string legacy_dir = sys.global_out_dir + "STRU/"; + sys.global_in_stru = find_md_restart_stru(legacy_dir, istep); + } } else { - sys.global_in_stru = inp.read_file_dir + "STRU_MD_" + std::to_string(istep); + // Restart from a user-provided directory: the previous run may + // have written either naming scheme there. + const std::string stru_dir = inp.read_file_dir; + sys.global_in_stru = find_md_restart_stru(stru_dir, istep); + } + + if (sys.global_in_stru.empty()) + { + const std::string message = "no STRU file found for MD restart in " + sys.global_readin_dir + + "; expected STRU_MD_{step} or STRU{step+1}"; + ModuleBase::WARNING_QUIT("ReadInput::set_global_dir", message); } } else diff --git a/source/source_io/test_serial/io_sys_var_test.cpp b/source/source_io/test_serial/io_sys_var_test.cpp index 5156027973b..21257b96694 100644 --- a/source/source_io/test_serial/io_sys_var_test.cpp +++ b/source/source_io/test_serial/io_sys_var_test.cpp @@ -5,6 +5,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "source_base/fs_compat.h" #include "source_base/tool_quit.h" #include "source_io/module_parameter/input_parameter.h" #define private public @@ -110,3 +111,55 @@ TEST_F(InputTest, Item_test) EXPECT_EQ(sys.npol, 1); } } + +TEST_F(InputTest, SetGlobalDirMdRestart) +{ + readinput.check_ntype_flag = false; + + const std::string case_dir = testing::TempDir() + "/abacus_md_restart_io/"; + ModuleBase::make_directory(case_dir); + + // Restart from a user-provided read_file_dir holding the legacy + // "STRU_MD_{istep}" naming: the structure file must be picked up. + const std::string legacy_dir = case_dir + "legacy/"; + ModuleBase::make_directory(legacy_dir); + const std::string restart_txt = legacy_dir + "Restart_md.txt"; + std::ofstream restart_file(restart_txt.c_str()); + restart_file << 5 << std::endl; + restart_file << 0.03 << std::endl; + restart_file.close(); + const std::string stru_legacy = legacy_dir + "STRU_MD_5"; + std::ofstream stru_file_legacy(stru_legacy.c_str()); + stru_file_legacy << "ATOMIC_SPECIES" << std::endl; + stru_file_legacy.close(); + + input.suffix = "autotest"; + input.calculation = "md"; + input.mdp.md_restart = true; + input.read_file_dir = legacy_dir; + TestParameters::set_global_dir(readinput, input, sys); + EXPECT_EQ(sys.global_in_stru, stru_legacy); + + // Restart from a read_file_dir holding the current "STRU{istep + 1}" + // naming written by run_md (1-based, unified with relax). + const std::string newnaming_dir = case_dir + "newnaming/"; + ModuleBase::make_directory(newnaming_dir); + const std::string restart_txt_new = newnaming_dir + "Restart_md.txt"; + std::ofstream restart_file_new(restart_txt_new.c_str()); + restart_file_new << 5 << std::endl; + restart_file_new << 0.03 << std::endl; + restart_file_new.close(); + const std::string stru_new = newnaming_dir + "STRU6"; + std::ofstream stru_file_new(stru_new.c_str()); + stru_file_new << "ATOMIC_SPECIES" << std::endl; + stru_file_new.close(); + + input.read_file_dir = newnaming_dir; + TestParameters::set_global_dir(readinput, input, sys); + EXPECT_EQ(sys.global_in_stru, stru_new); + + // restore the shared input state so other tests are not affected + input.calculation = "scf"; + input.mdp.md_restart = false; + input.read_file_dir = "OUT.$suffix"; +} From 432649cf479b476036b11282c2a43a27b8765783 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 13:18:07 +0800 Subject: [PATCH 14/30] fix(io): address review comments on out_app_flag mode-2 rework - add PSI/ candidate in read_wfc_nao so RT-TDDFT restart finds mode-2 snapshots - route H(k) term outputs to MAT directory; add out_hsr_npz to MAT dir creation predicate; narrow DOS dir creation predicate to out_dos/out_ldos - pass boolean append to save_mat in write_hk_common (was int out_app_flag) - update out_app_flag/out_wfc_lcao INPUT metadata and position_matrix docs - add mode-2 directory mapping and PSI layout roundtrip unit tests --- .../elec_properties/position_matrix.md | 2 +- source/source_esolver/esolver_ks_lcaopw.cpp | 2 +- .../module_ctrl/ctrl_runner_lcao.cpp | 4 +- .../source_io/module_ctrl/ctrl_scf_lcao.cpp | 16 +-- source/source_io/module_dhs/write_dh.cpp | 3 +- source/source_io/module_hs/hsk_writer.cpp | 7 +- source/source_io/module_hs/hterm_writer.cpp | 2 +- source/source_io/module_hs/vxc_op_mat.cpp | 3 +- .../module_parameter/read_inp_out.cpp | 33 ++++-- .../source_io/module_parameter/read_input.cpp | 8 +- source/source_io/module_wf/read_wfc_nao.cpp | 77 ++++++++++-- source/source_io/test/read_wfc_nao_test.cpp | 112 ++++++++++++++++++ .../source_io/test_serial/io_sys_var_test.cpp | 28 +++++ .../source_io/test_serial/read_input_test.cpp | 40 +++++++ 14 files changed, 303 insertions(+), 34 deletions(-) diff --git a/docs/advanced/elec_properties/position_matrix.md b/docs/advanced/elec_properties/position_matrix.md index 38743d5b65b..ccd71f56dcf 100644 --- a/docs/advanced/elec_properties/position_matrix.md +++ b/docs/advanced/elec_properties/position_matrix.md @@ -18,7 +18,7 @@ Each file or each section of the appended file starts with "STEP: " followed by Each block here contains the matrix for the corresponding cell. There are three columns in each block, giving the matrix elements in x, y, z directions, respectively. There are altogether nbasis * nbasis lines in each block, which emulates the matrix elements. -In molecular dynamics (MD) calculations, if [out_app_flag](../input_files/input-main.md#out_app_flag) is set to 1, then `data-rR-tr` is written in an append manner. Otherwise, output files will be put in a separate directory, `matrix`, and named as `$x`_data-rR-tr, where `$x` is the number of MD step. In addition, the output frequency is controlled by [out_freq_ion](../input_files/input-main.md#out_freq_ion). For example, if we are running a 10-step MD with out_freq_ion = 3, then `$x` will be 0, 3, 6, and 9. +In molecular dynamics (MD) calculations, the output frequency is controlled by [out_freq_ion](../input_files/input-main.md#out_freq_ion). Each ionic step is written to its own file named `rrg${step}_nao.txt`, where `${step}` is the one-based ionic-step index, while the single-shot output of `calculation = get_s` is named `rr_nao.txt`. The files are stored in `OUT.${suffix}` when [out_app_flag](../input_files/input-main.md#out_app_flag) is 0 or 1, and in its `MAT/` subdirectory when out_app_flag is 2. For example, if we are running a 10-step MD with out_freq_ion = 3 and out_app_flag = 2, the files are `MAT/rrg1_nao.txt`, `MAT/rrg4_nao.txt`, `MAT/rrg7_nao.txt`, and `MAT/rrg10_nao.txt`. ## get_s We also offer the option of only calculating the position matrix without running SCF. For that purpose, in `INPUT` file we need to set the keyword [calculation](../input_files/input-main.md#calculation) to `get_s`, and [out_mat_r](../input_files/input-main.md#out_mat_r) to `true`. diff --git a/source/source_esolver/esolver_ks_lcaopw.cpp b/source/source_esolver/esolver_ks_lcaopw.cpp index 5c6ba6e9549..f5ece6c3be7 100644 --- a/source/source_esolver/esolver_ks_lcaopw.cpp +++ b/source/source_esolver/esolver_ks_lcaopw.cpp @@ -299,7 +299,7 @@ namespace ModuleESolver this->kv, this->pelec->wg, PARAM.globalv.gamma_only_local, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_mat_dir, this->inp_->out_ndigits, this->inp_->ks_solver, cal_exx, diff --git a/source/source_io/module_ctrl/ctrl_runner_lcao.cpp b/source/source_io/module_ctrl/ctrl_runner_lcao.cpp index 55a21bce7f7..f2f6079d860 100644 --- a/source/source_io/module_ctrl/ctrl_runner_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_runner_lcao.cpp @@ -84,7 +84,7 @@ void ctrl_runner_lcao(UnitCell& ucell, // unitcell gd, inp.dft_plus_u, PARAM.globalv.gamma_only_local, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_mat_dir, inp.out_ndigits, inp.ks_solver, cal_exx, @@ -119,7 +119,7 @@ void ctrl_runner_lcao(UnitCell& ucell, // unitcell kv, orb.cutoffs(), gd, - PARAM.globalv.global_out_dir, + PARAM.globalv.global_mat_dir, cal_exx, hybrid_alpha, real_number diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index 12f9067348f..203d1de0695 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -207,7 +207,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, if (hsk_out_type == 1 || hsk_out_type == 2) { const int precision = inp.out_hsk[1]; - ModuleIO::write_hsk(global_out_dir, + ModuleIO::write_hsk(global_mat_dir, nspin, kv.get_nks(), kv.get_nkstot(), @@ -282,7 +282,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, ModuleIO::write_hsr(hr_vec, sr, &ucell, inp.out_hsr[0], precision, pv, (out_app_flag == 1), gamma_only, ucell.get_iat2iwt(), ucell.nat, istep, - PARAM.globalv.global_out_dir, pelec->eferm, + global_mat_dir, pelec->eferm, ofs_running); } @@ -292,7 +292,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, const bool output_hsr_npz = inp.out_hsr[0] == 3 || inp.out_hsr_npz_compat; if (output_hsr_npz) { - std::string zipname = PARAM.globalv.global_out_dir + "sr_nao.npz"; + std::string zipname = global_mat_dir + "sr_nao.npz"; ModuleIO::output_mat_npz(ucell, zipname, *(p_hamilt->getSR())); } @@ -302,7 +302,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, for (int ispin = 0; ispin < hr_vec.size(); ++ispin) { std::string zipname - = PARAM.globalv.global_out_dir + "hrs" + std::to_string(ispin + 1) + "_nao.npz"; + = global_mat_dir + "hrs" + std::to_string(ispin + 1) + "_nao.npz"; ModuleIO::output_mat_npz(ucell, zipname, *(hr_vec[ispin])); } } @@ -313,7 +313,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, for (int ispin = 0; ispin < dmr_vec.size(); ++ispin) { std::string zipname - = PARAM.globalv.global_out_dir + "output_DM" + std::to_string(ispin) + ".npz"; + = global_mat_dir + "output_DM" + std::to_string(ispin) + ".npz"; ModuleIO::output_mat_npz(ucell, zipname, *(dmr_vec[ispin])); } } @@ -455,7 +455,9 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, h_params.gga_grad = PARAM.inp.gga_grad; h_params.out_app_flag = out_app_flag; h_params.calculation = inp.calculation; - h_params.global_out_dir = global_out_dir; + // H(k) component matrices follow the selected matrix directory so + // they land in MAT/ together with the other matrix outputs in mode 2 + h_params.global_out_dir = global_mat_dir; h_params.global_matrix_dir = PARAM.globalv.global_matrix_dir; h_params.ks_solver = PARAM.inp.ks_solver; h_params.drank = GlobalV::DRANK; @@ -511,7 +513,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, const int out_label = 1; // 1: .txt, 2: .dat - std::string t_fn = ModuleIO::filename_output(global_out_dir, + std::string t_fn = ModuleIO::filename_output(global_mat_dir, "tk", "nao", ik, diff --git a/source/source_io/module_dhs/write_dh.cpp b/source/source_io/module_dhs/write_dh.cpp index e0d99fde3ee..d05ff12716a 100644 --- a/source/source_io/module_dhs/write_dh.cpp +++ b/source/source_io/module_dhs/write_dh.cpp @@ -123,13 +123,14 @@ void write_dh_perI(WriteDHParams& params, fk += "_ik" + std::to_string(params.kv->ik2iktot[ik]); } fk += "_nao.txt"; + const bool append_fk = (out_app_flag == 1); ModuleIO::save_mat(params.istep, hk.data(), nlocal, false, 8, false, - (out_app_flag == 1), + append_fk, fk, pv, GlobalV::DRANK, diff --git a/source/source_io/module_hs/hsk_writer.cpp b/source/source_io/module_hs/hsk_writer.cpp index 0faa1ba1aa7..8b0242cf0b6 100644 --- a/source/source_io/module_hs/hsk_writer.cpp +++ b/source/source_io/module_hs/hsk_writer.cpp @@ -40,6 +40,9 @@ void ModuleIO::write_hsk( { p_hamilt->updateHk(ik); const bool binary = (out_type == 2); + // save_mat takes a Boolean append flag: only mode 1 appends, modes + // 0 and 2 overwrite their per-step files + const bool append_mat = (out_app_flag == 1); hamilt::MatrixBlock h_mat; hamilt::MatrixBlock s_mat; @@ -56,7 +59,7 @@ void ModuleIO::write_hsk( binary, precision, 1, - out_app_flag, + append_mat, h_fn, pv, drank, @@ -83,7 +86,7 @@ void ModuleIO::write_hsk( binary, precision, 1, - out_app_flag, + append_mat, s_fn, pv, drank, diff --git a/source/source_io/module_hs/hterm_writer.cpp b/source/source_io/module_hs/hterm_writer.cpp index 95910677d11..3e3c7a00204 100644 --- a/source/source_io/module_hs/hterm_writer.cpp +++ b/source/source_io/module_hs/hterm_writer.cpp @@ -154,7 +154,7 @@ static void write_hk_common(hamilt::HContainer& hR, false, 8, false, - out_app_flag, + append, fname, pv, drank, diff --git a/source/source_io/module_hs/vxc_op_mat.cpp b/source/source_io/module_hs/vxc_op_mat.cpp index b749e110b5a..a8f95c42d84 100644 --- a/source/source_io/module_hs/vxc_op_mat.cpp +++ b/source/source_io/module_hs/vxc_op_mat.cpp @@ -116,6 +116,7 @@ void write_Vxc(const int nspin, const int istep = -1; const int out_label = 1; // 1 means .txt while 2 means .dat const int out_app_flag = 0; + const bool append_vxc = (out_app_flag == 1); std::string vxc_file = ModuleIO::filename_output( global_out_dir, "vxc", "nao", ik, kv.ik2iktot, nspin, kv.get_nkstot(), @@ -127,7 +128,7 @@ void write_Vxc(const int nspin, false /*binary*/, out_ndigits, true /*triangle*/, - out_app_flag /*append*/, + append_vxc, vxc_file, p2d, drank, diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index 5d172868e6d..904a87123d4 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -297,7 +297,7 @@ With `init_wfc file binary`, ABACUS reads only unindexed binary `wf*_pw.dat` fil item.annotation = "ouput LCAO wave functions, 0, no output 1: text, 2: binary"; item.category = "Output information"; item.type = "Integer"; - item.description = R"(Whether to output the electronic wavefunction coefficients into files and store them in the folder OUT.${suffix}. The files are named as wf{s}{spin index}{k(optional)}{k-point index}{g(optional)}{geometry index1}{_nao} + {".txt"/".dat"}. Here, 's' refers to spin, where s1 means spin up channel while s2 means spin down channel, and 's12' refer to spinor wave functions that contains both spin channels with spin-orbital coupling or noncollinear calculations enabled. In addition, if 'gamma_only' is set to 0, then the optinoal k-point sampling index appears with the k-point index attached to the electronic wave function file names. Finally, if out_app_flag is set to false, the file name contains the optional 'g' index for each ionic step that may have different geometries, and if out_app_flag is set to true, the wave functions accumulate during ionic steps. If the out_app_flag is set to false, a new folder named WFC will be created, and the wave function files will be saved into it. + item.description = R"(Whether to output the electronic wavefunction coefficients into files and store them in the folder OUT.${suffix} (or its PSI/ subdirectory when out_app_flag is 2). The files are named as wf{s}{spin index}{k(optional)}{k-point index}{g(optional)}{geometry index1}{_nao} + {".txt"/".dat"}. Here, 's' refers to spin, where s1 means spin up channel while s2 means spin down channel, and 's12' refer to spinor wave functions that contains both spin channels with spin-orbital coupling or noncollinear calculations enabled. In addition, if 'gamma_only' is set to 0, then the optinoal k-point sampling index appears with the k-point index attached to the electronic wave function file names. Finally, if out_app_flag is not 1, the file name contains the optional 'g' index for each ionic step that may have different geometries, and if out_app_flag is 1, the wave functions accumulate during ionic steps. When out_app_flag is 2, the wave function files are saved into the PSI/ subdirectory of OUT.${suffix}. * 0: no output * 1: (txt format) * gamma-only: wfs1_nao.txt or wfs2_nao.txt, ...; @@ -1391,19 +1391,38 @@ The circle order of the charge density on real space grids is: x is the outer lo } { Input_Item item("out_app_flag"); - item.annotation = "whether output r(R), H(R), S(R), T(R), and dH(R) " - "matrices in an append manner during MD"; + item.annotation = "output directory layout and append behavior " + "during multi-step calculations (0/1/2)"; item.category = "Output information"; item.type = "Integer"; - item.description = R"(Controls output file organization during multi-step calculations (MD, relax, RT-TDDFT). -* 0: Write to OUT directory directly, no append (files overwritten each step) + item.description = R"(Controls output file organization during multi-step calculations (MD, relax, RT-TDDFT). The directory routing applies to both LCAO and PW basis calculations. +* 0: Write to OUT directory directly, no append (each step's file carries a g{step} suffix) * 1: Append to same file (default, backward compatible) -* 2: Write to subdirectory (PSI/, CHG/, POT/, ELF/, DOS/, DIP/, MAT/) +* 2: Write to subdirectory (PSI/, CHG/, POT/, ELF/, DOS/, DIP/, MAT/), files carry a g{step} suffix +The legacy Boolean spellings (true/false, yes/no, on/off, t/f, y/n, and the dotted forms) are still accepted and map to modes 1/0 as before. For more information, refer to the documentation of out_mat_r, out_hsr, out_mat_t, out_mat_dh, out_hsk, out_wfc_lcao, out_chg, out_pot, out_elf, out_dos, out_dipole.)"; item.default_value = "1"; item.unit = ""; item.set_availability("basis_type==lcao and gamma_only==0"); - read_sync_int(input.out_app_flag); + // Keep the previously accepted Boolean spellings working: the legacy + // parser treated out_app_flag as a Boolean, so INPUT files containing + // "true"/"false"/"yes"/"no"/"on"/"off" (case-insensitive) must keep + // parsing instead of reaching std::stoi and throwing. + item.read_value = [](const Input_Item& item, Parameter& para) { + try + { + para.input.out_app_flag = std::stoi(item.str_values[0]); + } + catch (const std::invalid_argument&) + { + para.input.out_app_flag = assume_as_boolean(item.str_values[0]) ? 1 : 0; + } + catch (const std::out_of_range&) + { + para.input.out_app_flag = assume_as_boolean(item.str_values[0]) ? 1 : 0; + } + }; + sync_int(input.out_app_flag); item.check_value = [](const Input_Item& item, const Parameter& para) { if (para.input.out_app_flag < 0 || para.input.out_app_flag > 2) { diff --git a/source/source_io/module_parameter/read_input.cpp b/source/source_io/module_parameter/read_input.cpp index 56b4b750146..366082cf54e 100644 --- a/source/source_io/module_parameter/read_input.cpp +++ b/source/source_io/module_parameter/read_input.cpp @@ -314,16 +314,18 @@ void ReadInput::create_directory(const Parameter& param) {param.input.out_stru > 0, param.globalv.global_stru_dir}, {param.input.out_wfc_lcao > 0 || param.input.out_wfc_pw > 0, param.globalv.global_psi_dir}, {param.input.out_chg[0] > 0, param.globalv.global_chg_dir}, - {param.input.out_pot[0] > 0 || param.input.out_elf[0] > 0, param.globalv.global_pot_dir}, + {param.input.out_pot[0] > 0, param.globalv.global_pot_dir}, {param.input.out_elf[0] > 0, param.globalv.global_elf_dir}, - {param.input.out_dos > 0 || !param.input.out_band.empty() && param.input.out_band[0] > 0, param.globalv.global_dos_dir}, + {param.input.out_dos > 0 + || (!param.input.out_ldos.empty() && param.input.out_ldos[0] > 0), + param.globalv.global_dos_dir}, {param.input.out_dipole, param.globalv.global_dip_dir}, {param.input.out_dmk[0] > 0 || param.input.out_dmr[0] > 0 || param.input.out_hsk[0] > 0 || param.input.out_mat_hs2[0] > 0 || param.input.out_mat_r[0] > 0 || param.input.out_mat_t[0] > 0 || param.input.out_mat_tk[0] > 0 || param.input.out_mat_l[0] > 0 || param.input.out_mat_dh[0] > 0 || param.input.out_mat_ds[0] > 0 || param.input.out_mat_xc || param.input.out_mat_xc2[0] > 0 || param.input.out_hr_npz - || param.input.out_dm_npz, param.globalv.global_mat_dir} + || param.input.out_hsr_npz || param.input.out_dm_npz, param.globalv.global_mat_dir} }; for (const auto& flag_dir : subdir_flags) { diff --git a/source/source_io/module_wf/read_wfc_nao.cpp b/source/source_io/module_wf/read_wfc_nao.cpp index 58599769d4c..d814a1c52bb 100644 --- a/source/source_io/module_wf/read_wfc_nao.cpp +++ b/source/source_io/module_wf/read_wfc_nao.cpp @@ -8,6 +8,7 @@ #include "source_base/global_function.h" // use READ_VALUE #include +#include namespace { @@ -296,18 +297,78 @@ bool ModuleIO::read_wfc_nao( { if (myrank == 0) { - const int out_app_flag = 0; std::stringstream error_message; - std::string readin_dir = global_readin_dir; - if(istep >= 0) + const int read_type = binary ? 2 : 1; + // Candidate file locations, in priority order: + // 1. readin_dir + step-suffixed name: layout written by runs with + // out_app_flag 0 (files in the OUT directory root) + // 2. readin_dir + "PSI/" + step-suffixed name: layout written by + // runs with out_app_flag 2 + // 3. readin_dir + "WFC/" + step-suffixed name: layout written by + // releases before the out_app_flag rework + // 4. readin_dir + name without the step suffix: written in append + // mode (out_app_flag 1) + std::vector candidates; + if (istep >= 0) { - readin_dir = readin_dir + "WFC/"; + const int suffix_app_flag = 0; + candidates.push_back(ModuleIO::filename_output(global_readin_dir, + "wf", + "nao", + ik, + ik2iktot, + nspin, + nkstot, + read_type, + suffix_app_flag, + gamma_only, + istep)); + const std::string psi_dir = global_readin_dir + "PSI/"; + candidates.push_back(ModuleIO::filename_output(psi_dir, + "wf", + "nao", + ik, + ik2iktot, + nspin, + nkstot, + read_type, + suffix_app_flag, + gamma_only, + istep)); + const std::string legacy_dir = global_readin_dir + "WFC/"; + candidates.push_back(ModuleIO::filename_output(legacy_dir, + "wf", + "nao", + ik, + ik2iktot, + nspin, + nkstot, + read_type, + suffix_app_flag, + gamma_only, + istep)); } - const int read_type = binary ? 2 : 1; - std::string ss = ModuleIO::filename_output(readin_dir,"wf","nao", - ik,ik2iktot,nspin,nkstot,read_type,out_app_flag,gamma_only,istep); + const int append_app_flag = 1; + candidates.push_back(ModuleIO::filename_output(global_readin_dir, + "wf", + "nao", + ik, + ik2iktot, + nspin, + nkstot, + read_type, + append_app_flag, + gamma_only, + istep)); - read_success = read_one_file(ss, error_message, ik, ctot); + for (size_t ic = 0; ic < candidates.size(); ++ic) + { + read_success = read_one_file(candidates[ic], error_message, ik, ctot); + if (read_success) + { + break; + } + } errors = error_message.str(); } #ifdef __MPI diff --git a/source/source_io/test/read_wfc_nao_test.cpp b/source/source_io/test/read_wfc_nao_test.cpp index 6107db23036..2d2a9e4fa30 100644 --- a/source/source_io/test/read_wfc_nao_test.cpp +++ b/source/source_io/test/read_wfc_nao_test.cpp @@ -451,6 +451,118 @@ TEST_F(ReadWfcNaoTest, RejectTruncatedBinary) true)); } +TEST_F(ReadWfcNaoTest, ReadPsiDirectoryLayout) +{ + // RT-TDDFT restart with out_app_flag = 2: the snapshot lives in the + // PSI/ subdirectory of the read-in directory and carries the one-based + // g{step} suffix in its name. + const int nbands = 2; + const int nlocal = 3; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + const std::string psi_dir = binary_test_dir + "PSI/"; + if (my_rank == 0) + { + ModuleBase::GlobalFunc::MAKE_DIR(psi_dir); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + const std::vector coefficients = {0.1, 0.2, 0.3, 1.1, 1.2, 1.3}; + ModuleBase::matrix ekb_source(1, nbands); + ModuleBase::matrix wg_source(1, nbands); + ekb_source(0, 0) = -0.6; + ekb_source(0, 1) = 0.9; + wg_source(0, 0) = 2.0; + wg_source(0, 1) = 0.0; + + const int istep = 0; + const std::vector ik2iktot = {0}; + const int read_type = 2; + const int suffix_app_flag = 0; + // The real-valued reader builds candidate names through the gamma-only + // branch of filename_output, so the snapshot must use the same naming. + const bool gamma_only_for_name = true; + const std::string snapshot = ModuleIO::filename_output(psi_dir, + "wf", + "nao", + 0, + ik2iktot, + 1, + 1, + read_type, + suffix_app_flag, + gamma_only_for_name, + istep); + if (my_rank == 0) + { + ModuleIO::wfc_nao_write2file(snapshot, + coefficients.data(), + nlocal, + 0, + ekb_source, + wg_source, + true, + false); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + EXPECT_TRUE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + true, + 0, + istep)); + EXPECT_DOUBLE_EQ(ekb(0, 0), ekb_source(0, 0)); + EXPECT_DOUBLE_EQ(ekb(0, 1), ekb_source(0, 1)); + if (my_rank == 0) + { + EXPECT_DOUBLE_EQ(psi_read(0, 0, 0), coefficients[0]); + } + + // Rewriting the snapshot must replace it, not append another record. + if (my_rank == 0) + { + std::ifstream ifs(snapshot.c_str(), std::ios::binary); + ifs.seekg(0, std::ios::end); + const std::streamoff size_after_first_write = ifs.tellg(); + ifs.close(); + + ModuleIO::wfc_nao_write2file(snapshot, + coefficients.data(), + nlocal, + 0, + ekb_source, + wg_source, + true, + false); + + std::ifstream ifs_rewritten(snapshot.c_str(), std::ios::binary); + ifs_rewritten.seekg(0, std::ios::end); + const std::streamoff size_after_second_write = ifs_rewritten.tellg(); + ifs_rewritten.close(); + EXPECT_EQ(size_after_second_write, size_after_first_write); + + std::remove(snapshot.c_str()); + std::remove(psi_dir.c_str()); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif +} + #ifdef __MPI diff --git a/source/source_io/test_serial/io_sys_var_test.cpp b/source/source_io/test_serial/io_sys_var_test.cpp index 21257b96694..2fe0fa4ba6d 100644 --- a/source/source_io/test_serial/io_sys_var_test.cpp +++ b/source/source_io/test_serial/io_sys_var_test.cpp @@ -163,3 +163,31 @@ TEST_F(InputTest, SetGlobalDirMdRestart) input.mdp.md_restart = false; input.read_file_dir = "OUT.$suffix"; } + +TEST_F(InputTest, SetGlobalDirMode2) +{ + readinput.check_ntype_flag = false; + + // out_app_flag = 2 routes every output class to its unified + // subdirectory below OUT./ + input.suffix = "modetwo"; + input.out_app_flag = 2; + TestParameters::set_global_dir(readinput, input, sys); + + EXPECT_EQ(sys.global_out_dir, "OUT.modetwo/"); + EXPECT_EQ(sys.global_stru_dir, "OUT.modetwo/STRU/"); + EXPECT_EQ(sys.global_psi_dir, "OUT.modetwo/PSI/"); + EXPECT_EQ(sys.global_chg_dir, "OUT.modetwo/CHG/"); + EXPECT_EQ(sys.global_pot_dir, "OUT.modetwo/POT/"); + EXPECT_EQ(sys.global_elf_dir, "OUT.modetwo/ELF/"); + EXPECT_EQ(sys.global_dos_dir, "OUT.modetwo/DOS/"); + EXPECT_EQ(sys.global_dip_dir, "OUT.modetwo/DIP/"); + EXPECT_EQ(sys.global_mat_dir, "OUT.modetwo/MAT/"); + // legacy aliases follow the unified directories + EXPECT_EQ(sys.global_wfc_dir, sys.global_psi_dir); + EXPECT_EQ(sys.global_matrix_dir, sys.global_mat_dir); + + // restore the shared input state so other tests are not affected + input.suffix = "test"; + input.out_app_flag = 1; +} diff --git a/source/source_io/test_serial/read_input_test.cpp b/source/source_io/test_serial/read_input_test.cpp index b948aa9116c..bc84cbd1925 100644 --- a/source/source_io/test_serial/read_input_test.cpp +++ b/source/source_io/test_serial/read_input_test.cpp @@ -396,3 +396,43 @@ TEST_F(InputTest, Check) EXPECT_THAT(output, testing::HasSubstr("INPUT parameters have been successfully checked!")); EXPECT_TRUE(std::remove("./INPUT.ref") == 0); } + +TEST_F(InputTest, OutAppFlagParsing) +{ + // numeric modes 0/1/2 are parsed as integers + { + Parameter param; + read_parameters("app_flag_numeric_INPUT", "basis_type lcao\nout_app_flag 2\n", param); + EXPECT_EQ(param.inp.out_app_flag, 2); + } + { + Parameter param; + read_parameters("app_flag_zero_INPUT", "basis_type lcao\nout_app_flag 0\n", param); + EXPECT_EQ(param.inp.out_app_flag, 0); + } + // the legacy Boolean spellings remain valid: true-like maps to mode 1, + // false-like maps to mode 0 + const std::vector> spellings + = {{"true", 1}, + {"TRUE", 1}, + {"Yes", 1}, + {"ON", 1}, + {".true.", 1}, + {"t", 1}, + {"false", 0}, + {"False", 0}, + {"NO", 0}, + {"off", 0}, + {".false.", 0}, + {"F", 0}}; + for (const auto& spelling : spellings) + { + Parameter param; + read_parameters("app_flag_bool_INPUT", "basis_type lcao\nout_app_flag " + spelling.first + "\n", param); + EXPECT_EQ(param.inp.out_app_flag, spelling.second) << "spelling: " << spelling.first; + } + // values outside 0/1/2 are rejected + expect_invalid_input("app_flag_oob_INPUT", + "basis_type lcao\nout_app_flag 3\n", + "out_app_flag should be 0, 1, or 2"); +} From 7e0ac771366d2325d412c3940f54ef22c0fd114c Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:02:44 +0800 Subject: [PATCH 15/30] feat(io): honor out_app_flag=1 for realspace cube outputs In append mode (out_app_flag=1), chg/tau/pot/potes/ELF cube outputs now reuse a fixed filename (e.g. chgs1.cube, elftot.cube) so every step overwrites the previous snapshot instead of accumulating g{N}-suffixed files. Modes 0 and 2 keep the per-step g{N} suffix. --- source/source_io/module_ctrl/ctrl_output_fp.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 8e673e4fb4b..35d17a13eb5 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -56,7 +56,12 @@ void ctrl_output_fp(UnitCell& ucell, bool should_output = (output_frequency == 0); if (istep_in >= 0) { - geom_block = "g" + std::to_string(istep + 1); + // In append mode (out_app_flag == 1) each cube output reuses a + // fixed filename, so every step overwrites the previous snapshot. + if (out_app_flag != 1) + { + geom_block = "g" + std::to_string(istep + 1); + } should_output = true; } From ca73e2e3e55ffd27863b9333e9fb4be3456c522b Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:06:38 +0800 Subject: [PATCH 16/30] feat(io): write DM(k) to fixed filename when out_app_flag=1 Add an explicit out_app_flag parameter to ModuleIO::write_dmk. In append mode the geometry-step index is dropped from the filename (istep_file = -1) so every ionic step overwrites the previous snapshot, matching the fixed name that read_dmk already expects; the file body still records the real ionic step. --- source/source_io/module_ctrl/ctrl_scf_lcao.cpp | 2 +- source/source_io/module_dm/test/write_dmk_test.cpp | 4 ++-- source/source_io/module_dm/write_dmk.cpp | 10 +++++++++- source/source_io/module_dm/write_dmk.h | 3 +++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index 8e4621ee0ed..9ff021bf95f 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -197,7 +197,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, } const int precision = inp.out_dmk[1]; - ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, ofs_running); + ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, out_app_flag, ofs_running); } //------------------------------------------------------------------ diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index 4d6a2a8f1bc..6b552788306 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -165,8 +165,8 @@ TEST(DMKTest,WriteDMK) { kv.kd_done = true; std::ofstream ofs_log; - ModuleIO::write_dmk(dmk, kv, 3, efs, ucell, pv, out_dir, istep, ofs_log); - ModuleIO::write_dmk(dmk_multik, kv, 3, efs, ucell, pv, out_dir, istep, ofs_log); + ModuleIO::write_dmk(dmk, kv, 3, efs, ucell, pv, out_dir, istep, 0, ofs_log); + ModuleIO::write_dmk(dmk_multik, kv, 3, efs, ucell, pv, out_dir, istep, 0, ofs_log); std::ifstream ifs; diff --git a/source/source_io/module_dm/write_dmk.cpp b/source/source_io/module_dm/write_dmk.cpp index beec822652a..c57b8f196b9 100644 --- a/source/source_io/module_dm/write_dmk.cpp +++ b/source/source_io/module_dm/write_dmk.cpp @@ -188,11 +188,17 @@ void ModuleIO::write_dmk(const std::vector>& dmk, const Parallel_2D& pv, const std::string& dmk_dir, const int istep, + const int out_app_flag, std::ofstream& ofs_running) { ModuleBase::TITLE("ModuleIO", "write_dmk"); ModuleBase::timer::start("ModuleIO", "write_dmk"); + // In append mode (out_app_flag == 1) DM(k) reuses a fixed filename, + // so each ionic step overwrites the previous snapshot; the file body + // still records the real ionic step via istep. + const int istep_file = (out_app_flag == 1) ? -1 : istep; + int my_rank = 0; #ifdef __MPI MPI_Comm_rank(pv.comm(), &my_rank); @@ -239,7 +245,7 @@ void ModuleIO::write_dmk(const std::vector>& dmk, if (my_rank == 0) { std::string fn = dmk_dir - + dmk_gen_fname(gamma_only, ispin, nspin, ik, istep); + + dmk_gen_fname(gamma_only, ispin, nspin, ik, istep_file); std::ofstream ofs(fn.c_str()); @@ -347,6 +353,7 @@ template void ModuleIO::write_dmk(const std::vector> const Parallel_2D& pv, const std::string& dmk_dir, const int istep, + const int out_app_flag, std::ofstream& ofs_running); template void ModuleIO::write_dmk>(const std::vector>>& dmk, @@ -357,5 +364,6 @@ template void ModuleIO::write_dmk>(const std::vector void write_dmk(const std::vector>& dmk, @@ -78,6 +80,7 @@ void write_dmk(const std::vector>& dmk, const Parallel_2D& pv, const std::string& dmk_dir, const int istep, + const int out_app_flag, std::ofstream& ofs_running); } // namespace ModuleIO From 0a8c069f076b9bf5f6173408bad3d0aa01f66a97 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:08:39 +0800 Subject: [PATCH 17/30] feat(io): keep fixed T(R)/R(R) filenames when out_app_flag=1 Drop the g{N} suffix from tr_nao.csr and rr_nao.txt in append mode. The filenames were changing every ionic step, which defeated the existing MD append branches in save_sparse and the rr assemble path; with fixed names those branches now accumulate all steps as designed. --- source/source_io/module_hs/hsr_legacy.cpp | 4 +++- source/source_io/module_hs/pos_op_writer.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/source_io/module_hs/hsr_legacy.cpp b/source/source_io/module_hs/hsr_legacy.cpp index 8b3fc5a4dfc..d5b814214e9 100644 --- a/source/source_io/module_hs/hsr_legacy.cpp +++ b/source/source_io/module_hs/hsr_legacy.cpp @@ -174,7 +174,9 @@ void ModuleIO::output_TR(const int istep, ModuleBase::timer::start("ModuleIO", "output_TR"); std::stringstream sst; - if (istep >= 0) + // In append mode (out_app_flag == 1) the file keeps the fixed name so + // the MD append logic in save_sparse can accumulate all ionic steps. + if (istep >= 0 && options.out_app_flag != 1) { // insert the 1-indexed ionic-step suffix right after the "tr" prefix, // i.e. tr_nao.csr -> trg{step+1}_nao.csr (aligned with lxg/rrg naming) diff --git a/source/source_io/module_hs/pos_op_writer.cpp b/source/source_io/module_hs/pos_op_writer.cpp index 8f75d52ed72..79ce0355926 100644 --- a/source/source_io/module_hs/pos_op_writer.cpp +++ b/source/source_io/module_hs/pos_op_writer.cpp @@ -216,7 +216,9 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, if (GlobalV::DRANK == 0) { std::stringstream ssr; - if (step >= 0) + // In append mode (out_app_flag == 1) keep the fixed filename so + // the append branch below can accumulate all ionic steps. + if (step >= 0 && out_app_flag != 1) { ssr << global_matrix_dir << "rrg" << (step + 1) << "_nao.txt"; } From 93425349cc0d6aab2b173b5e8ddd5646a2ed1bc0 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:11:12 +0800 Subject: [PATCH 18/30] fix(io): use real out_app_flag for PW wavefunction output Replace the hardcoded out_app_flag=0 in write_wfc_pw (a 2025-05-17 TODO) with an explicit parameter wired from inp.out_app_flag. In the default append mode the PW wavefunction files now use fixed names (wfk1_pw.dat), which finally matches what the restart reader read_wf2rho_pw looks for; modes 0 and 2 keep the g{N} suffix. --- source/source_io/module_ctrl/ctrl_output_pw.cpp | 1 + source/source_io/module_wf/write_wfc_pw.cpp | 2 +- source/source_io/module_wf/write_wfc_pw.h | 1 + source/source_io/test/read_wf2rho_pw_test.cpp | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/source_io/module_ctrl/ctrl_output_pw.cpp b/source/source_io/module_ctrl/ctrl_output_pw.cpp index 6856a49458e..5bffea81abe 100644 --- a/source/source_io/module_ctrl/ctrl_output_pw.cpp +++ b/source/source_io/module_ctrl/ctrl_output_pw.cpp @@ -73,6 +73,7 @@ void ModuleIO::ctrl_iter_pw(const int istep, GlobalV::RANK_IN_POOL, GlobalV::NPROC_IN_POOL, inp.out_wfc_pw, + inp.out_app_flag, inp.ecutwfc, PARAM.globalv.global_psi_dir, stp.psi_cpu[0], diff --git a/source/source_io/module_wf/write_wfc_pw.cpp b/source/source_io/module_wf/write_wfc_pw.cpp index d90f7f5d645..4d322c83f54 100644 --- a/source/source_io/module_wf/write_wfc_pw.cpp +++ b/source/source_io/module_wf/write_wfc_pw.cpp @@ -23,6 +23,7 @@ void ModuleIO::write_wfc_pw( const int rank_in_pool, const int nproc_in_pool, const int out_wfc_pw, + const int out_app_flag, const double& ecutwfc, const std::string& global_out_dir, const psi::Psi>& psi, @@ -43,7 +44,6 @@ void ModuleIO::write_wfc_pw( assert(nkstot>0); assert(nks>0); - int out_app_flag = 0; // need to modify later, mohan 2025-05-17 bool gamma_only = false; // need to modify later, mohan 2025-05-17 std::string* wfilename = new std::string[nks]; diff --git a/source/source_io/module_wf/write_wfc_pw.h b/source/source_io/module_wf/write_wfc_pw.h index 7e035bf82cd..6877179eed7 100644 --- a/source/source_io/module_wf/write_wfc_pw.h +++ b/source/source_io/module_wf/write_wfc_pw.h @@ -19,6 +19,7 @@ void write_wfc_pw( const int rank_in_pool, const int nproc_in_pool, const int out_wfc_pw, + const int out_app_flag, const double& ecutwfc, const std::string& global_out_dir, const psi::Psi>& psi, diff --git a/source/source_io/test/read_wf2rho_pw_test.cpp b/source/source_io/test/read_wf2rho_pw_test.cpp index 991a46a8f26..eb43411bc61 100644 --- a/source/source_io/test/read_wf2rho_pw_test.cpp +++ b/source/source_io/test/read_wf2rho_pw_test.cpp @@ -298,6 +298,7 @@ TEST_F(ReadWfcRhoTest, ReadWfcRho) GlobalV::RANK_IN_POOL, GlobalV::NPROC_IN_POOL, TestParameters::input().out_wfc_pw, + 0, ecutwfc, out_dir, *psi, From d0274b95387109f9d94307fcd1319a28568f0e8d Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:12:52 +0800 Subject: [PATCH 19/30] feat(io): omit geometry index in init cube names when out_app_flag=1 write_chg_init/write_pot_init now keep the fixed _ini.cube names in append mode so each snapshot overwrites the previous one, instead of accumulating chgs1g{N}_ini.cube / potg{N}_ini.cube files. These initial-density cubes are write-only snapshots; no reader consumes them, so no restart path is affected. --- source/source_estate/write_init.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/source_estate/write_init.cpp b/source/source_estate/write_init.cpp index 6b083c1986a..d496e5eecaa 100644 --- a/source/source_estate/write_init.cpp +++ b/source/source_estate/write_init.cpp @@ -6,6 +6,7 @@ // - Frequency is out_freq_td for RT-TDDFT and out_freq_ion otherwise. // - Frequency = 0: chg_ini.cube/pot_ini.cube, chgs1_ini.cube/chgs2_ini.cube // - Frequency > 0: chgg{#}_ini.cube/potg{#}_ini.cube, chgs{#}g{#}_ini.cube +// (out_app_flag == 1 keeps the fixed names without the g{#} index) // - File indices start from 1 (istep + 1); RT-TDDFT uses electronic steps. // // Usage: @@ -82,7 +83,9 @@ void ModuleIO::write_chg_init(const UnitCell& ucell, if (should_output) { - const bool include_geom_step = (output_frequency > 0); + // In append mode (out_app_flag == 1) each snapshot overwrites + // the same fixed file, so the geometry-step index is omitted. + const bool include_geom_step = (output_frequency > 0) && (inp.out_app_flag != 1); for (int is = 0; is < nspin; is++) { @@ -151,7 +154,9 @@ void ModuleIO::write_pot_init(const UnitCell& ucell, if (should_output) { - const bool include_geom_step = (output_frequency > 0); + // In append mode (out_app_flag == 1) each snapshot overwrites + // the same fixed file, so the geometry-step index is omitted. + const bool include_geom_step = (output_frequency > 0) && (inp.out_app_flag != 1); for (int is = 0; is < nspin; is++) { From b8236ecb0f598fb5d1a171797deb035199fe0a3a Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:16:10 +0800 Subject: [PATCH 20/30] docs(input): describe fixed-name behavior of out_app_flag=1 Clarify that mode 1 reuses fixed filenames across steps (text records append, cube snapshots overwrite), fix the remaining Boolean wording in out_hsk, and note that out_chg=2/out_pot=3 initial-density cubes omit the geometry-step index in append mode. --- source/source_io/module_parameter/read_inp_out.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index 904a87123d4..37fb09f98ac 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -91,7 +91,7 @@ void ReadInput::item_output() - nspin = 2: `chgs1g{geom_step}_ini.cube` and `chgs2g{geom_step}_ini.cube`; - nspin = 4: `chgs1g{geom_step}_ini.cube`, `chgs2g{geom_step}_ini.cube`, `chgs3g{geom_step}_ini.cube`, and `chgs4g{geom_step}_ini.cube`. - output every out_freq_ion steps - Here, {geom_step} denotes the geometry step index, starting from 1 (geom_step = istep + 1). + Here, {geom_step} denotes the geometry step index, starting from 1 (geom_step = istep + 1). With out_app_flag = 1, the geometry-step index is omitted and each snapshot overwrites the same fixed file. - -1: Disable the charge density auto-back-up file `{suffix}-CHARGE-DENSITY.restart`, useful for large systems. The second integer controls the precision of the charge density output. If not given, `3` is used as default. For restarting from this file and other high-precision calculations, `10` is recommended. @@ -152,7 +152,7 @@ In molecular dynamics simulations, the output frequency is controlled by out_fre * nspin = 2: `pots1g{geom_step}_ini.cube` and `pots2g{geom_step}_ini.cube`; * nspin = 4: `pots1g{geom_step}_ini.cube`, `pots2g{geom_step}_ini.cube`, `pots3g{geom_step}_ini.cube`, and `pots4g{geom_step}_ini.cube`. * output every out_freq_ion steps - Here, {geom_step} denotes the geometry step index, starting from 1 (geom_step = istep + 1). + Here, {geom_step} denotes the geometry step index, starting from 1 (geom_step = istep + 1). With out_app_flag = 1, the geometry-step index is omitted and each snapshot overwrites the same fixed file. The optional second integer controls the output precision. If not provided, the default precision is 8. @@ -562,8 +562,8 @@ The output is also controlled by out_freq_ion and out_app_flag. For more informa * Multi-k, nspin = 2: hk1s1_nao.txt and hk1s2_nao.txt for the two spin channels of the Hamiltonian matrix, and sk1_nao.txt for the overlap matrix at the first k-point. Only one overlap matrix is written because it is identical for both spin channels. * Multi-k, nspin = 4: hk1s4_nao.txt for the spinor Hamiltonian matrix and sk1_nao.txt for the spinor overlap matrix at the first k-point. For binary output, the same names use the .dat suffix. Each native binary record contains the matrix dimension as an int followed by the row-major upper triangle. Gamma-only elements are doubles; multi-k and spinor elements are pairs of doubles containing the real and imaginary parts. Native integer representation and byte order are used. -When out_app_flag is true, the first ionic step truncates the file and later steps append complete records. -When out_app_flag is false, g followed by the one-based ionic-step index is inserted before _nao, for example hk1s1g1_nao.txt. +When out_app_flag is 1, the first ionic step truncates the file and later steps append complete records. +When out_app_flag is 0 or 2, g followed by the one-based ionic-step index is inserted before _nao, for example hk1s1g1_nao.txt. [NOTE] In the 3.10-LTS version, the file names are data-0-H and data-0-S, etc.)"; item.default_value = "0 8"; @@ -1397,7 +1397,7 @@ The circle order of the charge density on real space grids is: x is the outer lo item.type = "Integer"; item.description = R"(Controls output file organization during multi-step calculations (MD, relax, RT-TDDFT). The directory routing applies to both LCAO and PW basis calculations. * 0: Write to OUT directory directly, no append (each step's file carries a g{step} suffix) -* 1: Append to same file (default, backward compatible) +* 1: Reuse fixed filenames across steps (default, backward compatible): text outputs append new records, cube snapshots are overwritten * 2: Write to subdirectory (PSI/, CHG/, POT/, ELF/, DOS/, DIP/, MAT/), files carry a g{step} suffix The legacy Boolean spellings (true/false, yes/no, on/off, t/f, y/n, and the dotted forms) are still accepted and map to modes 1/0 as before. For more information, refer to the documentation of out_mat_r, out_hsr, out_mat_t, out_mat_dh, out_hsk, out_wfc_lcao, out_chg, out_pot, out_elf, out_dos, out_dipole.)"; From 8c9091d6528e126c7fb20c87c1736960ee895e9b Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:29:52 +0800 Subject: [PATCH 21/30] fix(io): append DM(k) records in MD when out_app_flag=1 The fixed filename alone still truncated the file every ionic step. Add a calculation parameter and open the stream with ios::app under the same condition as open_sparse_file (calculation=="md" && out_app_flag==1 && istep>=0), so each MD step appends a new record while other calculations start fresh. --- source/source_io/module_ctrl/ctrl_scf_lcao.cpp | 2 +- source/source_io/module_dm/test/write_dmk_test.cpp | 4 ++-- source/source_io/module_dm/write_dmk.cpp | 14 +++++++++++++- source/source_io/module_dm/write_dmk.h | 3 +++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index 9ff021bf95f..e0dfe6d1667 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -197,7 +197,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, } const int precision = inp.out_dmk[1]; - ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, out_app_flag, ofs_running); + ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, out_app_flag, inp.calculation, ofs_running); } //------------------------------------------------------------------ diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index 6b552788306..8990f4f68ed 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -165,8 +165,8 @@ TEST(DMKTest,WriteDMK) { kv.kd_done = true; std::ofstream ofs_log; - ModuleIO::write_dmk(dmk, kv, 3, efs, ucell, pv, out_dir, istep, 0, ofs_log); - ModuleIO::write_dmk(dmk_multik, kv, 3, efs, ucell, pv, out_dir, istep, 0, ofs_log); + ModuleIO::write_dmk(dmk, kv, 3, efs, ucell, pv, out_dir, istep, 0, "scf", ofs_log); + ModuleIO::write_dmk(dmk_multik, kv, 3, efs, ucell, pv, out_dir, istep, 0, "scf", ofs_log); std::ifstream ifs; diff --git a/source/source_io/module_dm/write_dmk.cpp b/source/source_io/module_dm/write_dmk.cpp index c57b8f196b9..e3774e213e4 100644 --- a/source/source_io/module_dm/write_dmk.cpp +++ b/source/source_io/module_dm/write_dmk.cpp @@ -189,6 +189,7 @@ void ModuleIO::write_dmk(const std::vector>& dmk, const std::string& dmk_dir, const int istep, const int out_app_flag, + const std::string& calculation, std::ofstream& ofs_running) { ModuleBase::TITLE("ModuleIO", "write_dmk"); @@ -198,6 +199,9 @@ void ModuleIO::write_dmk(const std::vector>& dmk, // so each ionic step overwrites the previous snapshot; the file body // still records the real ionic step via istep. const int istep_file = (out_app_flag == 1) ? -1 : istep; + // Each MD step appends a new record to the fixed file, mirroring the + // open_sparse_file convention; other calculations start fresh. + const bool append_on_restart = (calculation == "md") && (out_app_flag == 1) && (istep >= 0); int my_rank = 0; #ifdef __MPI @@ -247,7 +251,13 @@ void ModuleIO::write_dmk(const std::vector>& dmk, std::string fn = dmk_dir + dmk_gen_fname(gamma_only, ispin, nspin, ik, istep_file); - std::ofstream ofs(fn.c_str()); + std::ofstream ofs; + std::ios_base::openmode mode = std::ios::out; + if (append_on_restart) + { + mode |= std::ios::app; + } + ofs.open(fn.c_str(), mode); ofs << " --- Ionic Step " << istep+1 << " ---" << std::endl; @@ -354,6 +364,7 @@ template void ModuleIO::write_dmk(const std::vector> const std::string& dmk_dir, const int istep, const int out_app_flag, + const std::string& calculation, std::ofstream& ofs_running); template void ModuleIO::write_dmk>(const std::vector>>& dmk, @@ -365,5 +376,6 @@ template void ModuleIO::write_dmk>(const std::vector void write_dmk(const std::vector>& dmk, @@ -81,6 +83,7 @@ void write_dmk(const std::vector>& dmk, const std::string& dmk_dir, const int istep, const int out_app_flag, + const std::string& calculation, std::ofstream& ofs_running); } // namespace ModuleIO From a9cba384829f373b0ec25371f098e16a562c7961 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 14:39:03 +0800 Subject: [PATCH 22/30] docs(governance): require one-based ionic-step indices in outputs Rule 22: user-facing output files print ionic steps starting from 1, matching the g{N} filename suffix convention; zero-based counters such as "STEP: 0" are not allowed. --- AGENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 0705adefe89..f235f134264 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,6 +79,9 @@ rules. Read the complete governance document before making or reviewing changes: guards and `#ifdef`/`#ifndef` usages of existing macros are exempt. 21. Do not initialize member variables from `PARAM` in `.h`/`.hpp` files; pass the value through the constructor or a setter instead. + 22. Print ionic-step indices one-based (starting from 1) in user-facing + output files, matching the `g{N}` filename suffix convention; do not + write zero-based step counters such as `STEP: 0` into output files. - Use LF line endings for text files. Only `.bat` and `.cmd` files may use CRLF. - Keep source file additions deterministic: update the relevant `CMakeLists.txt` or explain why the file is generated or included indirectly. From a658239dcd1c5f557a69036d787b1cebde958f31 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 15:10:06 +0800 Subject: [PATCH 23/30] feat(io): full out_app_flag=1 semantics for DOS/PDOS/L/matrices Naming (mode 1 fixed name, modes 0/2 keep g{N}): - DOS (LCAO+PW) and PDOS honor out_app_flag for the g{N} suffix - lx/ly/lz matrices: Angmom_op::calculate takes out_app_flag Append (relax now appends like MD; was calculation=="md" only): - open_sparse_file, rr/dH/dS writers, DM(k): condition relaxed to (out_app_flag == 1) && (istep >= 0); SCF passes istep=-1, unaffected - PDOS and lx/ly/lz append per ionic step in mode 1 - DM(k) drops the calculation parameter again (no longer used) Step indexing (governance rule 22, one-based): - STEP: headers of sparse text files and the binary CSR step field now start from 1 (hs_sparse_io, pos_op_writer, dhs_sparse_writer, hsr_writer) Build: make -j 30 verified by developer. --- .../source_io/module_ctrl/ctrl_scf_lcao.cpp | 4 ++-- .../module_dm/test/write_dmk_test.cpp | 4 ++-- source/source_io/module_dm/write_dmk.cpp | 10 ++++------ source/source_io/module_dm/write_dmk.h | 6 ++---- .../source_io/module_dos/cal_pdos_gamma.cpp | 3 ++- source/source_io/module_dos/cal_pdos_gamma.h | 1 + .../source_io/module_dos/cal_pdos_multik.cpp | 3 ++- source/source_io/module_dos/cal_pdos_multik.h | 1 + .../unittests/test_write_pdos_text.cpp | 6 +++--- .../source_io/module_dos/write_dos_lcao.cpp | 5 ++++- source/source_io/module_dos/write_dos_pw.cpp | 4 +++- .../source_io/module_dos/write_pdos_text.cpp | 19 ++++++++++++++---- source/source_io/module_dos/write_pdos_text.h | 5 +++-- source/source_io/module_hs/angmom_op_mat.cpp | 18 ++++++++++++----- source/source_io/module_hs/angmom_op_mat.h | 20 ++++++++++--------- .../source_io/module_hs/dhs_sparse_writer.cpp | 9 ++++++--- source/source_io/module_hs/hs_sparse_io.cpp | 8 ++++++-- source/source_io/module_hs/hsr_writer.cpp | 3 ++- source/source_io/module_hs/pos_op_writer.cpp | 7 +++++-- 19 files changed, 87 insertions(+), 49 deletions(-) diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index e0dfe6d1667..0ee884ec369 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -197,7 +197,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, } const int precision = inp.out_dmk[1]; - ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, out_app_flag, inp.calculation, ofs_running); + ModuleIO::write_dmk(dm->get_dmk_vec(), kv, precision, efermis, &(ucell), pv, global_mat_dir, istep, out_app_flag, ofs_running); } //------------------------------------------------------------------ @@ -562,7 +562,7 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, PARAM.globalv.gamma_only_local, &ofs_running, GlobalV::MY_RANK); - mylcalculator.calculate(inp.suffix, global_mat_dir, ucell, inp.out_mat_l[1], GlobalV::MY_RANK, istep_in); + mylcalculator.calculate(inp.suffix, global_mat_dir, ucell, inp.out_mat_l[1], GlobalV::MY_RANK, istep_in, out_app_flag); } //------------------------------------------------------------------ diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index 8990f4f68ed..6b552788306 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -165,8 +165,8 @@ TEST(DMKTest,WriteDMK) { kv.kd_done = true; std::ofstream ofs_log; - ModuleIO::write_dmk(dmk, kv, 3, efs, ucell, pv, out_dir, istep, 0, "scf", ofs_log); - ModuleIO::write_dmk(dmk_multik, kv, 3, efs, ucell, pv, out_dir, istep, 0, "scf", ofs_log); + ModuleIO::write_dmk(dmk, kv, 3, efs, ucell, pv, out_dir, istep, 0, ofs_log); + ModuleIO::write_dmk(dmk_multik, kv, 3, efs, ucell, pv, out_dir, istep, 0, ofs_log); std::ifstream ifs; diff --git a/source/source_io/module_dm/write_dmk.cpp b/source/source_io/module_dm/write_dmk.cpp index e3774e213e4..b75114d3b48 100644 --- a/source/source_io/module_dm/write_dmk.cpp +++ b/source/source_io/module_dm/write_dmk.cpp @@ -189,7 +189,6 @@ void ModuleIO::write_dmk(const std::vector>& dmk, const std::string& dmk_dir, const int istep, const int out_app_flag, - const std::string& calculation, std::ofstream& ofs_running) { ModuleBase::TITLE("ModuleIO", "write_dmk"); @@ -199,9 +198,10 @@ void ModuleIO::write_dmk(const std::vector>& dmk, // so each ionic step overwrites the previous snapshot; the file body // still records the real ionic step via istep. const int istep_file = (out_app_flag == 1) ? -1 : istep; - // Each MD step appends a new record to the fixed file, mirroring the - // open_sparse_file convention; other calculations start fresh. - const bool append_on_restart = (calculation == "md") && (out_app_flag == 1) && (istep >= 0); + // Each multi-step run (MD or relax) appends a new record to the fixed + // file, mirroring the open_sparse_file convention; single-point SCF + // passes istep = -1 and starts a fresh file. + const bool append_on_restart = (out_app_flag == 1) && (istep >= 0); int my_rank = 0; #ifdef __MPI @@ -364,7 +364,6 @@ template void ModuleIO::write_dmk(const std::vector> const std::string& dmk_dir, const int istep, const int out_app_flag, - const std::string& calculation, std::ofstream& ofs_running); template void ModuleIO::write_dmk>(const std::vector>>& dmk, @@ -376,6 +375,5 @@ template void ModuleIO::write_dmk>(const std::vector void write_dmk(const std::vector>& dmk, @@ -83,7 +82,6 @@ void write_dmk(const std::vector>& dmk, const std::string& dmk_dir, const int istep, const int out_app_flag, - const std::string& calculation, std::ofstream& ofs_running); } // namespace ModuleIO diff --git a/source/source_io/module_dos/cal_pdos_gamma.cpp b/source/source_io/module_dos/cal_pdos_gamma.cpp index 3b9ee1b1dd6..d042fb6ede5 100644 --- a/source/source_io/module_dos/cal_pdos_gamma.cpp +++ b/source/source_io/module_dos/cal_pdos_gamma.cpp @@ -23,6 +23,7 @@ void ModuleIO::cal_pdos( const double& dos_edelta_ev, const double& bcoeff, const int istep, + const int out_app_flag, const int nlocal, const int nspin, const std::string& global_out_dir) @@ -148,7 +149,7 @@ void ModuleIO::cal_pdos( if (GlobalV::MY_RANK == 0) { write_pdos_text(ucell, pdos.data(), nspin, nlocal, npoints, - emin, dos_edelta_ev, global_out_dir, istep); + emin, dos_edelta_ev, global_out_dir, istep, out_app_flag); ModuleIO::write_orb_info(&ucell); } } diff --git a/source/source_io/module_dos/cal_pdos_gamma.h b/source/source_io/module_dos/cal_pdos_gamma.h index e5122464262..0af1fca0540 100644 --- a/source/source_io/module_dos/cal_pdos_gamma.h +++ b/source/source_io/module_dos/cal_pdos_gamma.h @@ -25,6 +25,7 @@ namespace ModuleIO const double& dos_edelta_ev, const double& bcoeff, const int istep, + const int out_app_flag, const int nlocal, const int nspin, const std::string& global_out_dir); diff --git a/source/source_io/module_dos/cal_pdos_multik.cpp b/source/source_io/module_dos/cal_pdos_multik.cpp index 95ee8c7ba1c..219b5bf69cb 100644 --- a/source/source_io/module_dos/cal_pdos_multik.cpp +++ b/source/source_io/module_dos/cal_pdos_multik.cpp @@ -22,6 +22,7 @@ void ModuleIO::cal_pdos( const double& dos_edelta_ev, const double& bcoeff, const int istep, + const int out_app_flag, const int nlocal, const int nspin, const std::string& global_out_dir) @@ -188,7 +189,7 @@ void ModuleIO::cal_pdos( if (GlobalV::MY_RANK == 0) { write_pdos_text(ucell, pdos.data(), nspin, nlocal, npoints, - emin, dos_edelta_ev, global_out_dir, istep); + emin, dos_edelta_ev, global_out_dir, istep, out_app_flag); ModuleIO::write_orb_info(&ucell); } } diff --git a/source/source_io/module_dos/cal_pdos_multik.h b/source/source_io/module_dos/cal_pdos_multik.h index 110009becb4..a4b194d967b 100644 --- a/source/source_io/module_dos/cal_pdos_multik.h +++ b/source/source_io/module_dos/cal_pdos_multik.h @@ -26,6 +26,7 @@ namespace ModuleIO const double& dos_edelta_ev, const double& bcoeff, const int istep, + const int out_app_flag, const int nlocal, const int nspin, const std::string& global_out_dir); diff --git a/source/source_io/module_dos/unittests/test_write_pdos_text.cpp b/source/source_io/module_dos/unittests/test_write_pdos_text.cpp index 00681a3cb5f..52571d2888f 100644 --- a/source/source_io/module_dos/unittests/test_write_pdos_text.cpp +++ b/source/source_io/module_dos/unittests/test_write_pdos_text.cpp @@ -93,7 +93,7 @@ TEST_F(WritePdosTextTest, BasicOutput) } ModuleIO::write_pdos_text(*ucell, &pdos, nspin, nlocal, npoints, - emin, dos_edelta_ev, out_dir, istep); + emin, dos_edelta_ev, out_dir, istep, 0); const std::string str = read_file("./pdoss1.txt"); @@ -127,7 +127,7 @@ TEST_F(WritePdosTextTest, IstepFileName) pdos.zero_out(); ModuleIO::write_pdos_text(*ucell, &pdos, nspin, nlocal, npoints, - emin, dos_edelta_ev, "./", 2); + emin, dos_edelta_ev, "./", 2, 0); const std::string str = read_file("./pdoss1g3.txt"); EXPECT_THAT(str, testing::HasSubstr("# istep: 3")); @@ -163,7 +163,7 @@ TEST_F(WritePdosTextTest, SpinPolarized) } ModuleIO::write_pdos_text(*ucell, pdos_arr, nspin, nlocal, npoints, - emin, dos_edelta_ev, "./", -1); + emin, dos_edelta_ev, "./", -1, 0); const std::string s1 = read_file("./pdoss1.txt"); const std::string s2 = read_file("./pdoss2.txt"); diff --git a/source/source_io/module_dos/write_dos_lcao.cpp b/source/source_io/module_dos/write_dos_lcao.cpp index 20ef0799aae..ed47e0c5eaf 100644 --- a/source/source_io/module_dos/write_dos_lcao.cpp +++ b/source/source_io/module_dos/write_dos_lcao.cpp @@ -62,7 +62,9 @@ void write_dos_lcao( ss << global_out_dir << "doss" << is + 1; - if(istep>=0) + // In append mode (out_app_flag == 1) the fixed name is reused and + // each ionic step appends a new DOS block; modes 0 and 2 keep g{N}. + if(istep>=0 && out_app_flag!=1) { ss << "g" << istep+1; } @@ -102,6 +104,7 @@ void write_dos_lcao( dos_edelta_ev, bcoeff, istep, + out_app_flag, nlocal, nspin, global_out_dir); diff --git a/source/source_io/module_dos/write_dos_pw.cpp b/source/source_io/module_dos/write_dos_pw.cpp index 3564a78f275..4026b181b60 100644 --- a/source/source_io/module_dos/write_dos_pw.cpp +++ b/source/source_io/module_dos/write_dos_pw.cpp @@ -54,7 +54,9 @@ void ModuleIO::write_dos_pw( std::stringstream ss; ss << global_out_dir << "doss" << is + 1; - if(istep_in>=0) + // In append mode (out_app_flag == 1) the fixed name is reused and + // each ionic step appends a new DOS block; modes 0 and 2 keep g{N}. + if(istep_in>=0 && out_app_flag!=1) { ss << "g" << istep_in+1; } diff --git a/source/source_io/module_dos/write_pdos_text.cpp b/source/source_io/module_dos/write_pdos_text.cpp index f0b3331dcf1..84cfab41c85 100644 --- a/source/source_io/module_dos/write_pdos_text.cpp +++ b/source/source_io/module_dos/write_pdos_text.cpp @@ -17,24 +17,35 @@ void ModuleIO::write_pdos_text( const double& emin, const double& dos_edelta_ev, const std::string& out_dir, - const int istep) + const int istep, + const int out_app_flag) { ModuleBase::TITLE("ModuleIO", "write_pdos_text"); // number of spin channels to write const int nspin0 = (nspin == 2) ? 2 : 1; + // In append mode (out_app_flag == 1) reuse the fixed filename and append + // each ionic step; single-point SCF passes istep = -1 and truncates. + const int istep_file = (out_app_flag == 1) ? -1 : istep; + const bool append_step = (out_app_flag == 1) && (istep >= 0); + for (int is = 0; is < nspin0; ++is) { std::stringstream ss; ss << out_dir << "pdoss" << is + 1; - if (istep >= 0) + if (istep_file >= 0) { - ss << "g" << istep + 1; + ss << "g" << istep_file + 1; } ss << ".txt"; - std::ofstream ofs(ss.str().c_str()); + std::ios_base::openmode mode = std::ios::out; + if (append_step) + { + mode |= std::ios::app; + } + std::ofstream ofs(ss.str().c_str(), mode); // iw2m stores m as 0..2l, mapping to physical m values: // 0->0, 1->+1, 2->-1, 3->+2, 4->-2, 5->+3, 6->-3 diff --git a/source/source_io/module_dos/write_pdos_text.h b/source/source_io/module_dos/write_pdos_text.h index dcb9a184623..434c6f7ebe9 100644 --- a/source/source_io/module_dos/write_pdos_text.h +++ b/source/source_io/module_dos/write_pdos_text.h @@ -8,7 +8,7 @@ namespace ModuleIO { /// Write PDOS in plain text long-table format. -/// File name: pdoss{spin}g{geom}.txt +/// File name: pdoss{spin}g{geom}.txt (g{geom} omitted when out_app_flag == 1) /// Columns: energy(eV) atom species l z m pdos(1/eV) /// For nspin=4, the two spinor components are summed. void write_pdos_text( @@ -20,7 +20,8 @@ void write_pdos_text( const double& emin, const double& dos_edelta_ev, const std::string& out_dir, - const int istep); + const int istep, + const int out_app_flag); } diff --git a/source/source_io/module_hs/angmom_op_mat.cpp b/source/source_io/module_hs/angmom_op_mat.cpp index 16f8dc7e44d..a0cfa54e57a 100644 --- a/source/source_io/module_hs/angmom_op_mat.cpp +++ b/source/source_io/module_hs/angmom_op_mat.cpp @@ -380,7 +380,8 @@ void ModuleIO::Angmom_op::calculate( const UnitCell& ucell, const int precision, const int rank, - const int istep) + const int istep, + const int out_app_flag) { ModuleBase::TITLE("Angmom_op", "calculate"); ModuleBase::timer::start("Angmom_op", "calculate"); @@ -392,9 +393,11 @@ void ModuleIO::Angmom_op::calculate( } std::ofstream ofout; const std::string dir = "xyz"; - /// @brief Filename suffix: if istep >= 0, append "g{istep+1}" (e.g., "g1") - /// to follow the out_freq_ion convention; otherwise no suffix. - const std::string step_suffix = (istep >= 0) ? "g" + std::to_string(istep + 1) : ""; + /// @brief Filename suffix: in append mode (out_app_flag == 1) the fixed + /// name is reused and each ionic step appends; modes 0 and 2 carry + /// "g{istep+1}" (e.g., "g1") following the out_freq_ion convention. + const std::string step_suffix = (istep >= 0 && out_app_flag != 1) ? "g" + std::to_string(istep + 1) : ""; + const bool append_step = (out_app_flag == 1) && (istep >= 0); const std::string title = "# it ia il iz im iRx iRy iRz jt ja jl jz jm Re[] Im[]\n" "# it: atomtype index of the first atom\n" "# ia: atomic index of the first atom within the atomtype\n" @@ -413,7 +416,12 @@ void ModuleIO::Angmom_op::calculate( for (char d : dir) { std::string fn = outdir + "l" + d + step_suffix + "_nao.txt"; - ofout.open(fn, std::ios::out); + std::ios_base::openmode mode = std::ios::out; + if (append_step) + { + mode |= std::ios::app; + } + ofout.open(fn, mode); ofout << title; this->kernel(&ofout, ucell, d, precision); ofout.close(); diff --git a/source/source_io/module_hs/angmom_op_mat.h b/source/source_io/module_hs/angmom_op_mat.h index c5000d68f32..c79a059ff46 100644 --- a/source/source_io/module_hs/angmom_op_mat.h +++ b/source/source_io/module_hs/angmom_op_mat.h @@ -226,19 +226,21 @@ namespace ModuleIO * filenames are fixed as l{x,y,z}[g{step}]_nao.txt) * @param outdir the output directory * @param ucell the unit cell object - * @param precision the precision of the output, default is 10 - * @param rank the MPI rank, default is 0 - * @param istep the ionic step index. If >= 0, the filename includes - * `g{istep+1}` (e.g., lxg1_nao.txt); if -1, the filename - * has no step suffix (e.g., lx_nao.txt). Default is -1 - * (single-step overwrite mode). + * @param precision the precision of the output + * @param rank the MPI rank + * @param istep the ionic step index; -1 for single-point runs + * @param out_app_flag append mode: with 1 the fixed filename + * l{x,y,z}_nao.txt is reused and each step appends; + * with 0 or 2 the filename carries `g{istep+1}` + * (e.g., lxg1_nao.txt) */ void calculate(const std::string& prefix, const std::string& outdir, const UnitCell& ucell, - const int precision = 10, - const int rank = 0, - const int istep = -1); + const int precision, + const int rank, + const int istep, + const int out_app_flag); private: std::ofstream fallback_ofs_; diff --git a/source/source_io/module_hs/dhs_sparse_writer.cpp b/source/source_io/module_hs/dhs_sparse_writer.cpp index 557adfa59e7..636938c5f14 100644 --- a/source/source_io/module_hs/dhs_sparse_writer.cpp +++ b/source/source_io/module_hs/dhs_sparse_writer.cpp @@ -66,8 +66,9 @@ void ModuleIO::save_dH_sparse(const int& istep, const int total_R_num = static_cast(all_R_coor_ptr.size()); int output_R_number = 0; - // Use istep for filename/append decisions; clamp only the header STEP value. - const int header_step = std::max(istep, 0); + // Use istep for filename/append decisions; clamp only the header STEP + // value, printed one-based per governance rule 22. + const int header_step = std::max(istep, 0) + 1; int spin_loop = 1; if (nspin == 2) { @@ -132,7 +133,9 @@ void ModuleIO::save_dH_sparse(const int& istep, if (GlobalV::DRANK == 0) { - const bool open_in_append = (calculation == "md") && (out_app_flag == 1) && istep >= 0; + // Append for any multi-step run (MD or relax) in append mode; + // single-point SCF passes istep = -1 and starts a fresh file. + const bool open_in_append = (out_app_flag == 1) && istep >= 0; for (auto& comp: comps) { const std::string label = std::string("dH") + comp.axis; diff --git a/source/source_io/module_hs/hs_sparse_io.cpp b/source/source_io/module_hs/hs_sparse_io.cpp index 580903839cc..8f02178944e 100644 --- a/source/source_io/module_hs/hs_sparse_io.cpp +++ b/source/source_io/module_hs/hs_sparse_io.cpp @@ -33,7 +33,9 @@ void open_sparse_file(std::ofstream& ofs, const ModuleIO::SparseWriteOptions& op { mode |= std::ios::binary; } - const bool append_on_restart = (options.calculation == "md") && (options.out_app_flag == 1) && options.istep >= 0; + // Append for any multi-step run (MD or relax) in append mode; + // single-point SCF passes istep = -1 and always starts a fresh file. + const bool append_on_restart = (options.out_app_flag == 1) && (options.istep >= 0); if (append_on_restart) { mode |= std::ios::app; @@ -51,7 +53,9 @@ void write_sparse_header(std::ofstream& ofs, const int nlocal, const int output_R_number) { - const int step = std::max(options.istep, 0); + // ionic step printed one-based per governance rule 22, consistent + // with the g{N} filename suffix (g1 holds the first ionic step) + const int step = std::max(options.istep, 0) + 1; if (options.binary) { ofs.write(reinterpret_cast(&step), sizeof(int)); diff --git a/source/source_io/module_hs/hsr_writer.cpp b/source/source_io/module_hs/hsr_writer.cpp index 10b5218fa69..54975d9d1c3 100644 --- a/source/source_io/module_hs/hsr_writer.cpp +++ b/source/source_io/module_hs/hsr_writer.cpp @@ -219,7 +219,8 @@ void ModuleIO::write_hcontainer_csr_binary(const std::string& fname, } std::sort(R_coordinates.begin(), R_coordinates.end()); - const int step = std::max(istep, 0); + // ionic step written one-based per governance rule 22 + const int step = std::max(istep, 0) + 1; const int nbasis = mat_serial->get_nbasis(); const int nR = static_cast(R_coordinates.size()); write_native_value(ofs, step); diff --git a/source/source_io/module_hs/pos_op_writer.cpp b/source/source_io/module_hs/pos_op_writer.cpp index 79ce0355926..8095abab207 100644 --- a/source/source_io/module_hs/pos_op_writer.cpp +++ b/source/source_io/module_hs/pos_op_writer.cpp @@ -228,8 +228,11 @@ void PosOpWriter::out_lat_r(const UnitCell& ucell, } ofs_tem1.close(); - const bool open_in_append = (calculation == "md") && (out_app_flag == 1) && step >= 0; - const int header_step = std::max(step, 0); + // Append for any multi-step run (MD or relax) in append mode; + // single-point SCF passes step = -1 and starts a fresh file. + const bool open_in_append = (out_app_flag == 1) && step >= 0; + // ionic step printed one-based per governance rule 22 + const int header_step = std::max(step, 0) + 1; ModuleIO::detail::assemble_csr(ssr.str(), tem1.str(), header_step, From 9b2b4ac4db7017a17fae239053d4056ca50cacb6 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 15:43:52 +0800 Subject: [PATCH 24/30] feat(io): single accumulated STRU file when out_app_flag=1 Instead of numbered STRU{N} files, out_app_flag=1 now writes one fixed-name STRU file that accumulates one structure block per written step, for both relax and MD: - unitcell::print_stru_file and relax_stru_io::write_stru take a new append flag; relax writes "# Ionic Step N" headers (one-based, governance rule 22) via the existing build_stru_header - mdcell::print_stru_file takes frame_comment + append; MD frames are prefixed with "# MD step N". The MPI path broadcasts a base offset (lseek SEEK_END) and keeps absolute pwrite offsets; O_APPEND is deliberately avoided since Linux pwrite ignores the offset with it - CIF output keeps single-frame semantics: fixed-name STRU.cif is overwritten each written step - modes 0/2 keep the numbered STRU{N} files unchanged - CLI help of out_stru/out_app_flag documents the mode-1 behavior Build: make -j 30 verified by developer. --- .../test/test_mdcell_reader.cpp | 2 +- source/source_cell/print_cell.cpp | 36 ++++++++++++++----- source/source_cell/print_cell.h | 20 +++++++++-- .../source_cell/test/read_atoms_hlp_test.cpp | 2 +- source/source_cell/test/test_print_cell.cpp | 18 +++++----- .../module_parameter/read_inp_out.cpp | 6 ++-- source/source_md/run_md.cpp | 15 ++++++-- source/source_relax/relax_driver.cpp | 17 ++++++--- source/source_relax/relax_stru_io.cpp | 6 ++-- source/source_relax/relax_stru_io.h | 5 ++- 10 files changed, 94 insertions(+), 33 deletions(-) diff --git a/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp b/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp index 50c907ab4f0..ac191c2d4cf 100644 --- a/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp +++ b/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp @@ -227,7 +227,7 @@ TEST(MDCellReaderTest, RestartStruPreservesAtomRecordsAcrossRanks) StruMeta metadata; metadata.species.resize(2); const std::string output_file = "distributed_mdcell_restart.STRU"; - mdcell::print_stru_file(mdcell, metadata, output_file); + mdcell::print_stru_file(mdcell, metadata, output_file, "", false); DomainDecomposition reader_decomp; MDCell round_trip = MDCellReader::read_stru(output_file, diff --git a/source/source_cell/print_cell.cpp b/source/source_cell/print_cell.cpp index f98b3ab8a99..2697bd1a961 100644 --- a/source/source_cell/print_cell.cpp +++ b/source/source_cell/print_cell.cpp @@ -101,7 +101,8 @@ namespace unitcell const bool& dpks_desc, const int& iproc, const ModuleBase::matrix& force, - const bool& has_force) + const bool& has_force, + const bool append) { ModuleBase::TITLE("UnitCell","print_stru_file"); if (iproc != 0) @@ -235,7 +236,12 @@ namespace unitcell nat_++; } } - std::ofstream ofs(fn.c_str()); + std::ios_base::openmode mode = std::ios::out; + if (append) + { + mode |= std::ios::app; + } + std::ofstream ofs(fn.c_str(), mode); ofs << str; ofs.close(); return; @@ -368,7 +374,8 @@ StruMeta make_stru_meta(const UnitCell& ucell) namespace mdcell { -void print_stru_file(const MDCell& cell, const StruMeta& stru_meta, const std::string& fn) +void print_stru_file(const MDCell& cell, const StruMeta& stru_meta, const std::string& fn, + const std::string& frame_comment, const bool append) { if (stru_meta.species.size() != cell.type_labels().size() || stru_meta.species.size() != cell.type_masses().size() @@ -376,27 +383,35 @@ void print_stru_file(const MDCell& cell, const StruMeta& stru_meta, const std::s { throw std::runtime_error("MDCell STRU metadata does not match the MDCell type data."); } - const std::string header = mdcell_stru_header(cell, stru_meta); + const std::string header = frame_comment + mdcell_stru_header(cell, stru_meta); #ifdef __MPI int rank = 0; const MPI_Comm comm = cell.communicator(); MPI_Comm_rank(comm, &rank); int header_ok = 1; + MPI_Offset base_offset = 0; if (rank == 0) { - const int header_file = open(fn.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0666); + // No O_APPEND: on Linux pwrite() ignores the given offset when the + // file was opened with O_APPEND. Truncate unless appending, then + // seek to the current end to learn the base offset. + const int open_flags = O_CREAT | O_WRONLY | (append ? 0 : O_TRUNC); + const int header_file = open(fn.c_str(), open_flags, 0666); if (header_file < 0) { header_ok = 0; } else { - const bool header_written = write_at(header_file, header, 0); + const off_t end_pos = lseek(header_file, 0, SEEK_END); + base_offset = end_pos >= 0 ? static_cast(end_pos) : 0; + const bool header_written = end_pos >= 0 && write_at(header_file, header, base_offset); const bool header_closed = close(header_file) == 0; header_ok = header_written && header_closed; } } MPI_Bcast(&header_ok, 1, MPI_INT, 0, comm); + MPI_Bcast(&base_offset, 1, MPI_OFFSET, 0, comm); if (header_ok == 0) { throw std::runtime_error("Unable to create MDCell restart STRU file: " + fn + ": " + std::strerror(errno)); @@ -413,7 +428,7 @@ void print_stru_file(const MDCell& cell, const StruMeta& stru_meta, const std::s throw std::runtime_error("Unable to open MDCell restart STRU file: " + fn + ": " + std::strerror(errno)); } - MPI_Offset offset = static_cast(header.size()); + MPI_Offset offset = base_offset + static_cast(header.size()); for (std::size_t it = 0; it < stru_meta.species.size(); ++it) { const std::string type_header = mdcell_type_header(cell, stru_meta, it); @@ -453,7 +468,12 @@ void print_stru_file(const MDCell& cell, const StruMeta& stru_meta, const std::s throw std::runtime_error("Unable to close MDCell restart STRU file: " + fn + ": " + std::strerror(errno)); } #else - std::ofstream output(fn.c_str()); + std::ios_base::openmode mode = std::ios::out; + if (append) + { + mode |= std::ios::app; + } + std::ofstream output(fn.c_str(), mode); output << header; for (std::size_t it = 0; it < stru_meta.species.size(); ++it) output << mdcell_type_header(cell, stru_meta, it) << local_mdcell_atoms(cell, it); diff --git a/source/source_cell/print_cell.h b/source/source_cell/print_cell.h index 1370629b0cd..ab39ddcf0dc 100644 --- a/source/source_cell/print_cell.h +++ b/source/source_cell/print_cell.h @@ -51,6 +51,8 @@ namespace unitcell * @param iproc GlobalV::MY_RANK [in] * @param force atomic forces in Ry/Bohr, used only when has_force is true [in] * @param has_force true to emit per-atom f fields; force matrix must match nat [in] + * @param append true to append the structure block to an existing file + * (out_app_flag == 1 multi-step runs); false truncates [in] */ void print_stru_file(const UnitCell& ucell, const Atom* atoms, @@ -64,7 +66,8 @@ namespace unitcell const bool& dpks_desc, const int& iproc, const ModuleBase::matrix& force, - const bool& has_force); + const bool& has_force, + const bool append); /** * @brief Print basic unitcell information to output stream. @@ -79,9 +82,22 @@ namespace unitcell namespace mdcell { +/** + * @brief Write an MDCell structure file in STRU format. + * + * @param mdcell distributed MD cell data [in] + * @param stru_meta species/orbital metadata used for the header [in] + * @param fn output file path [in] + * @param frame_comment comment block prepended before the structure header + * (e.g. "# MD step N\n\n"); pass an empty string for no marker [in] + * @param append true to append the frame to an existing file + * (out_app_flag == 1 multi-step runs); false truncates [in] + */ void print_stru_file(const MDCell& mdcell, const StruMeta& stru_meta, - const std::string& fn); + const std::string& fn, + const std::string& frame_comment, + const bool append); } #endif diff --git a/source/source_cell/test/read_atoms_hlp_test.cpp b/source/source_cell/test/read_atoms_hlp_test.cpp index c536fa4eabf..194a9f67fc3 100644 --- a/source/source_cell/test/read_atoms_hlp_test.cpp +++ b/source/source_cell/test/read_atoms_hlp_test.cpp @@ -684,7 +684,7 @@ TEST_F(ReadAtomsHelperTest, RoundTripMultiAtomMixedFields) const std::string filename = "test_stru_roundtrip.tmp"; unitcell::print_stru_file(ucell, ucell.atoms, ucell.latvec, filename, "", - 2, false, false, false, false, 0, force, true); + 2, false, false, false, false, 0, force, true, false); std::ifstream ifpos(filename.c_str()); diff --git a/source/source_cell/test/test_print_cell.cpp b/source/source_cell/test/test_print_cell.cpp index b1d131ffcb4..9bcf7123f33 100644 --- a/source/source_cell/test/test_print_cell.cpp +++ b/source/source_cell/test/test_print_cell.cpp @@ -48,7 +48,7 @@ TEST_F(PrintCellTest, PrintSTRU_nspin1) std::string fn = "C1H2_STRU_nspin1"; unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 1, false, false, false, false, 0, ModuleBase::matrix(), false); + fn, "", 1, false, false, false, false, 0, ModuleBase::matrix(), false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -91,7 +91,7 @@ TEST_F(PrintCellTest, PrintSTRU_zero_force_computed) force.zero_out(); unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 2, false, false, false, false, 0, force, true); + fn, "", 2, false, false, false, false, 0, force, true, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -114,7 +114,7 @@ TEST_F(PrintCellTest, PrintSTRU_force_matrix_without_flag) force(0, 0) = 0.1; force(0, 1) = 0.2; force(0, 2) = 0.3; unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 2, false, false, false, false, 0, force, false); + fn, "", 2, false, false, false, false, 0, force, false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -131,7 +131,7 @@ TEST_F(PrintCellTest, PrintSTRU_nspin2_no_force) std::string fn = "C1H2_STRU_nspin2"; unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 2, true, false, false, false, 0, ModuleBase::matrix(), false); + fn, "", 2, true, false, false, false, 0, ModuleBase::matrix(), false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -163,7 +163,7 @@ TEST_F(PrintCellTest, PrintSTRU_nspin2_with_mag) ucell->atoms[1].mag[0] = -0.5; // H1 ucell->atoms[1].mag[1] = 2.0; // H2 unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 2, false, false, false, false, 0, ModuleBase::matrix(), false); + fn, "", 2, false, false, false, false, 0, ModuleBase::matrix(), false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -192,7 +192,7 @@ TEST_F(PrintCellTest, PrintSTRU_nspin2_mulliken) ucell->orbital_fn[1] = "__unittest_orbital_fn_1__"; ucell->atom_mulliken = {{-1, 0.5}, {-1, 0.4}, {-1, 0.3}}; unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 2, false, true, true, true, 0, ModuleBase::matrix(), false); + fn, "", 2, false, true, true, true, 0, ModuleBase::matrix(), false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -221,7 +221,7 @@ TEST_F(PrintCellTest, PrintSTRU_nspin4_initial_mag) ucell->atoms[1].m_loc_[0].set(0.0, 1.0, 0.0); // H1 ucell->atoms[1].m_loc_[1].set(0.0, 0.0, 1.0); // H2 unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 4, false, false, false, false, 0, ModuleBase::matrix(), false); + fn, "", 4, false, false, false, false, 0, ModuleBase::matrix(), false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -247,7 +247,7 @@ TEST_F(PrintCellTest, PrintSTRU_nspin4_mulliken) ucell->atom_mulliken = {{-1, 0.5, 0.1, 0.2}, {-1, 0.4, 0.3, 0.4}, {-1, 0.3, 0.5, 0.6}}; unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 4, false, true, false, false, 0, ModuleBase::matrix(), false); + fn, "", 4, false, true, false, false, 0, ModuleBase::matrix(), false, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); @@ -274,7 +274,7 @@ TEST_F(PrintCellTest, PrintSTRU_with_force) force(2, 0) = 0.05; force(2, 1) = 0.15; force(2, 2) = -0.25; // H2 unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, - fn, "", 2, false, false, false, false, 0, force, true); + fn, "", 2, false, false, false, false, 0, force, true, false); std::ifstream ifs; ifs.open(fn); std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index 37fb09f98ac..211be5716ee 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -458,8 +458,8 @@ Note: values 2 and 3 are no longer supported. Setting out_dos to 2 or 3 will rai item.type = "Integer"; item.description = "Controls the output of structure files per ionic step. The files are written to the OUT.{suffix}/ directory. Each file corresponds to the structure at the current ionic step, and includes a header comment with the ABACUS version, timestamp, energy, and stress tensor. When out_freq_ion is positive, the numbered files STRU{istep} (or STRU{istep}.cif) are written every out_freq_ion steps; when out_freq_ion is 0, no numbered files are output. This parameter is effective for scf/nscf/relax/cell-relax and md. For scf/nscf only STRU_FINAL (or STRU_FINAL.cif) is written. For MD, structure output follows the same out_freq_ion frequency as the restart file.\n" " - 0: No structure files are output.\n" - " - 1: ABACUS STRU format files are output. The latest structure is written to STRU_NOW (overwritten each step), the numbered file STRU{istep} (e.g., STRU1, STRU2) is written every out_freq_ion steps (when out_freq_ion is positive), and the final converged structure is written to STRU_FINAL. No CIF files are output.\n" - " - 2: CIF format files are output. The latest structure is written to STRU_NOW.cif (overwritten each step), the numbered file STRU{istep}.cif (e.g., STRU1.cif, STRU2.cif) is written every out_freq_ion steps (when out_freq_ion is positive), and the final converged structure is written to STRU_FINAL.cif. No non-CIF files are output.\n" + " - 1: ABACUS STRU format files are output. The latest structure is written to STRU_NOW (overwritten each step), the numbered file STRU{istep} (e.g., STRU1, STRU2) is written every out_freq_ion steps (when out_freq_ion is positive), and the final converged structure is written to STRU_FINAL. No CIF files are output. When out_app_flag is 1, the numbered STRU{istep} files are replaced by a single fixed-name STRU file that appends one structure block (with a one-based \"# Ionic Step N\" or \"# MD step N\" comment) per written step.\n" + " - 2: CIF format files are output. The latest structure is written to STRU_NOW.cif (overwritten each step), the numbered file STRU{istep}.cif (e.g., STRU1.cif, STRU2.cif) is written every out_freq_ion steps (when out_freq_ion is positive), and the final converged structure is written to STRU_FINAL.cif. No non-CIF files are output. When out_app_flag is 1, the numbered files are replaced by a fixed-name STRU.cif overwritten each written step.\n" "[NOTE] For backward compatibility, true/false (case insensitive) are accepted and converted to 1/0."; item.default_value = "1"; item.unit = ""; @@ -1400,7 +1400,7 @@ The circle order of the charge density on real space grids is: x is the outer lo * 1: Reuse fixed filenames across steps (default, backward compatible): text outputs append new records, cube snapshots are overwritten * 2: Write to subdirectory (PSI/, CHG/, POT/, ELF/, DOS/, DIP/, MAT/), files carry a g{step} suffix The legacy Boolean spellings (true/false, yes/no, on/off, t/f, y/n, and the dotted forms) are still accepted and map to modes 1/0 as before. -For more information, refer to the documentation of out_mat_r, out_hsr, out_mat_t, out_mat_dh, out_hsk, out_wfc_lcao, out_chg, out_pot, out_elf, out_dos, out_dipole.)"; +For more information, refer to the documentation of out_mat_r, out_hsr, out_mat_t, out_mat_dh, out_hsk, out_wfc_lcao, out_chg, out_pot, out_elf, out_dos, out_dipole, out_stru.)"; item.default_value = "1"; item.unit = ""; item.set_availability("basis_type==lcao and gamma_only==0"); diff --git a/source/source_md/run_md.cpp b/source/source_md/run_md.cpp index 1e92c2e9556..d16ac26135f 100644 --- a/source/source_md/run_md.cpp +++ b/source/source_md/run_md.cpp @@ -299,9 +299,20 @@ void md_line(MDCell& mdcell, if (param_in.inp.out_stru > 0) { const int stru_step = current_step + 1; + // out_app_flag == 1: single fixed-name STRU accumulates one + // frame per step with a one-based "# MD step N" comment; + // modes 0/2 keep the numbered STRU{stru_step} files. + const bool append_stru = (param_in.inp.out_app_flag == 1); + const std::string frame_comment = append_stru + ? "# MD step " + std::to_string(stru_step) + "\n\n" + : ""; std::stringstream file; - file << PARAM.globalv.global_stru_dir << "STRU" << stru_step; - mdcell::print_stru_file(mdcell, mdcell.stru_meta(), file.str()); + file << PARAM.globalv.global_stru_dir << "STRU"; + if (!append_stru) + { + file << stru_step; + } + mdcell::print_stru_file(mdcell, mdcell.stru_meta(), file.str(), frame_comment, append_stru); } mdrun->write_restart(PARAM.globalv.global_out_dir); } diff --git a/source/source_relax/relax_driver.cpp b/source/source_relax/relax_driver.cpp index 865ed78d729..f9afb041374 100644 --- a/source/source_relax/relax_driver.cpp +++ b/source/source_relax/relax_driver.cpp @@ -225,13 +225,20 @@ void Relax_Driver::stru_out(const int istep, need_orb, deepks_setorb_, my_rank_, - inp.cal_force); + inp.cal_force, + false); } // Numbered files per out_freq_ion: only meaningful for relaxation calculations if (is_relax && freq_ok) { - const std::string step_file = out_dir_ + "STRU" + std::to_string(istep + 1) + (inp.out_stru == 1 ? "" : ".cif"); + // out_app_flag == 1: single fixed-name STRU accumulates one block per + // step (CIF format truncates to the latest frame instead); modes 0/2 + // keep the numbered STRU{istep+1} files. + const bool append_stru = (inp.out_app_flag == 1) && (inp.out_stru == 1); + const std::string step_file = (inp.out_app_flag == 1) + ? out_dir_ + (inp.out_stru == 1 ? "STRU" : "STRU.cif") + : out_dir_ + "STRU" + std::to_string(istep + 1) + (inp.out_stru == 1 ? "" : ".cif"); relax_stru_io::write_stru(ucell, inp, step_file, @@ -240,7 +247,8 @@ void Relax_Driver::stru_out(const int istep, need_orb, deepks_setorb_, my_rank_, - inp.cal_force); + inp.cal_force, + append_stru); } } @@ -291,7 +299,8 @@ void Relax_Driver::final_out(const int istep, need_orb, deepks_setorb_, my_rank_, - write_force); + write_force, + false); } // relax_nmax == 0 is a valid dry-run mode: no relaxation step was ever diff --git a/source/source_relax/relax_stru_io.cpp b/source/source_relax/relax_stru_io.cpp index 0f191ac644b..21883283cd2 100644 --- a/source/source_relax/relax_stru_io.cpp +++ b/source/source_relax/relax_stru_io.cpp @@ -117,7 +117,8 @@ void write_stru(UnitCell& ucell, const bool need_orb, const bool deepks_setorb, const int my_rank, - const bool has_force) + const bool has_force, + const bool append) { if (inp.out_stru == 1) { @@ -133,7 +134,8 @@ void write_stru(UnitCell& ucell, deepks_setorb, my_rank, force, - has_force); + has_force, + append); } else if (inp.out_stru == 2) { diff --git a/source/source_relax/relax_stru_io.h b/source/source_relax/relax_stru_io.h index 2830f4b09b7..87e37882b07 100644 --- a/source/source_relax/relax_stru_io.h +++ b/source/source_relax/relax_stru_io.h @@ -64,6 +64,8 @@ bool need_orbital(const Input_para& inp); * @param has_force true to emit per-atom f fields; forwarded to * print_stru_file. Callers must pass false when the forces do not * belong to the geometry being written (e.g. early relax exit). + * @param append true to append the structure block (STRU format only, + * for out_app_flag == 1 multi-step runs); CIF always truncates. */ void write_stru(UnitCell& ucell, const Input_para& inp, @@ -73,7 +75,8 @@ void write_stru(UnitCell& ucell, const bool need_orb, const bool deepks_setorb, const int my_rank, - const bool has_force); + const bool has_force, + const bool append); } // namespace relax_stru_io #endif From 0933cf5bdf6ecde873bb3ab1d5f239f489c2422b Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 15:48:35 +0800 Subject: [PATCH 25/30] fix(io): terminate DM(k) matrix body with a newline The DM(k) writer emitted the row newline only before each group of elements, so the file ended without a trailing newline. In append mode (out_app_flag == 1) the next " --- Ionic Step N ---" record was glued to the last value of the previous step. Add a trailing newline before closing the file and cover it with a regression assertion. --- source/source_io/module_dm/test/write_dmk_test.cpp | 2 ++ source/source_io/module_dm/write_dmk.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index 6b552788306..ab5e5bfda9b 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -195,6 +195,8 @@ TEST(DMKTest,WriteDMK) { std::istreambuf_iterator()); EXPECT_THAT(str, testing::HasSubstr("0 # Fermi energy in Ry")); EXPECT_THAT(str, testing::HasSubstr("20 # number of localized basis")); + // the matrix body must end with a newline so appended records are not glued + EXPECT_EQ(str.back(), '\n'); ifs.close(); fn = "dmk2s1_nao.txt"; diff --git a/source/source_io/module_dm/write_dmk.cpp b/source/source_io/module_dm/write_dmk.cpp index b75114d3b48..9a162a8ba78 100644 --- a/source/source_io/module_dm/write_dmk.cpp +++ b/source/source_io/module_dm/write_dmk.cpp @@ -331,6 +331,10 @@ void ModuleIO::write_dmk(const std::vector>& dmk, } } } + // Terminate the matrix body with a newline so that the next + // appended record (out_app_flag == 1) starts on a fresh line + // instead of being glued to the last value of this step. + ofs << std::endl; ofs.close(); } // rank0 } // ik From 65a4a61126c6c94176a76e8924eb5829410d0327 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 15:52:42 +0800 Subject: [PATCH 26/30] feat(io): append PW wavefunction frames when out_app_flag=1 write_wfc_pw now mirrors write_wfc_nao's append_flag: the first written step truncates the fixed-name wf*_pw.{txt,dat} files and later ionic steps append one frame per step (all payload writes already use append mode; only the per-step pre-clear is skipped). Modes 0/2 keep per-step g{N} files unchanged. Also terminate the DM(k) matrix body with a newline so appended records start on a fresh line (developer fix), and document in read_wf2rho_pw/out_wfc_pw help that multi-frame binary files are read from their first frame only. Build: make -j 30 verified by developer. --- .../module_parameter/read_inp_out.cpp | 4 ++-- source/source_io/module_wf/read_wf2rho_pw.cpp | 5 ++++ source/source_io/module_wf/write_wfc_pw.cpp | 24 ++++++++++++------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index 211be5716ee..f869afb8f4f 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -271,9 +271,9 @@ Available values are: The file-name pattern is `wfk{k}[s{spin}][g{geometry step}][e{electronic iteration}]_pw.txt` for `out_wfc_pw=1` and `wfk{k}[s{spin}][g{geometry step}][e{electronic iteration}]_pw.dat` for `out_wfc_pw=2`. All PW output files include a `k*` label, including Gamma-only calculations. Without geometry-step or electronic-iteration indices, representative names are `wfk1_pw.txt` or `wfk1_pw.dat` for `nspin=1`, `wfk1s1_pw.txt` and `wfk1s2_pw.txt` or their `.dat` equivalents for `nspin=2`, and `wfk1s4_pw.txt` or `wfk1s4_pw.dat` for `nspin=4`. -With `out_freq_ion=0`, files are written only when the electronic calculation converges or reaches `scf_nmax`; no `g*` or `e*` index is added. During structural relaxation or molecular dynamics, later ionic steps overwrite the same unindexed files. With `out_freq_ion` > 0, output is restricted to the ionic steps selected by `out_freq_ion` and is written when the electronic iteration is a multiple of `out_freq_elec`, when the calculation converges, or when it reaches `scf_nmax`. Both `g*` and `e*` indices are then added, including for a static `calculation=scf` or `calculation=nscf` run. +With `out_freq_ion=0`, files are written only when the electronic calculation converges or reaches `scf_nmax`; no `g*` or `e*` index is added. During structural relaxation or molecular dynamics with `out_app_flag=1`, the first written step truncates the unindexed files and later ionic steps append one frame per step (mirroring `out_wfc_lcao`); with `out_app_flag=0` the unindexed files are overwritten each step. With `out_freq_ion` > 0, output is restricted to the ionic steps selected by `out_freq_ion` and is written when the electronic iteration is a multiple of `out_freq_elec`, when the calculation converges, or when it reaches `scf_nmax`. Both `g*` and `e*` indices are then added (except that `g*` is omitted when `out_app_flag=1`), including for a static `calculation=scf` or `calculation=nscf` run. -With `init_wfc file binary`, ABACUS reads only unindexed binary `wf*_pw.dat` files from `read_file_dir`. Such directly reusable files are normally generated with `out_wfc_pw=2` and `out_freq_ion=0`. Text `wf*_pw.txt` files and files containing `g*` or `e*` indices are not matched automatically. +With `init_wfc file binary`, ABACUS reads only unindexed binary `wf*_pw.dat` files from `read_file_dir`, starting from the first frame. Such directly reusable files are normally generated with `out_wfc_pw=2` and `out_freq_ion=0`; multi-frame files accumulated under `out_app_flag=1` are trajectories and only their first frame is visible to the reader. Text `wf*_pw.txt` files and files containing `g*` or `e*` indices are not matched automatically. [NOTE] In the 3.10-LTS version, the binary files are named `WAVEFUNC1.dat`, `WAVEFUNC2.dat`, etc.)"; item.default_value = "0"; diff --git a/source/source_io/module_wf/read_wf2rho_pw.cpp b/source/source_io/module_wf/read_wf2rho_pw.cpp index 298334607db..20d0415e874 100644 --- a/source/source_io/module_wf/read_wf2rho_pw.cpp +++ b/source/source_io/module_wf/read_wf2rho_pw.cpp @@ -128,6 +128,11 @@ void ModuleIO::read_wf2rho_pw_impl(const ModulePW::PW_Basis_K* pw_wfc, // mohan add 2025-05-17 // .dat file + // NOTE: reads the fixed-name binary file from its first frame. Files + // accumulated across ionic steps under out_app_flag == 1 hold one + // frame per step (MD trajectories); only the first frame is seen + // here, so use out_app_flag 0/2 (per-step files) when the charge + // density of a specific step is to be reconstructed. const int out_type = 2; const int out_app_flag = 0; const bool gamma_only = false; diff --git a/source/source_io/module_wf/write_wfc_pw.cpp b/source/source_io/module_wf/write_wfc_pw.cpp index 4d322c83f54..f7433de42ef 100644 --- a/source/source_io/module_wf/write_wfc_pw.cpp +++ b/source/source_io/module_wf/write_wfc_pw.cpp @@ -65,15 +65,23 @@ void ModuleIO::write_wfc_pw( if (rank_in_pool == 0) { - if (out_wfc_pw == 1) + // Mirrors write_wfc_nao's append_flag: the first step of a + // run truncates the file, later steps append a new frame + // when out_app_flag == 1, so the file accumulates one frame + // per ionic step instead of being rewritten each step. + const bool append_frame = (istep > 0 && out_app_flag == 1); + if (!append_frame) { - std::ofstream ofs(fn.c_str()); // clear all wavefunc files. - ofs.close(); - } - else if (out_wfc_pw == 2) - { - Binstream wfs(fn, "w"); - wfs.close(); + if (out_wfc_pw == 1) + { + std::ofstream ofs(fn.c_str()); // clear all wavefunc files. + ofs.close(); + } + else if (out_wfc_pw == 2) + { + Binstream wfs(fn, "w"); + wfs.close(); + } } } } From c14f0c81a858c0c5190b51706f90c9e34b309da2 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 16:14:11 +0800 Subject: [PATCH 27/30] feat(io): add per-ionic-step provenance header to band output band.txt/bands1.txt/bands2.txt contained numeric rows only, so the k-point blocks appended across ionic steps of a relax/md run were indistinguishable, and a single-step run carried no version or timestamp. Print the STRU-style header ("# ABACUS version", "# Written at", "# IONIC STEP n") in front of every ionic step instead: write_bands now takes the step index, nscf_bands takes the prepared header, and the step label is one-based with the collinear spin-channel tag when nspin=2. The integration comparison strips "#" lines before comparing band.txt, and the unit test skips comment lines while parsing and asserts the header of each appended block. --- .../output_files/output-specification.md | 17 +++- source/source_esolver/esolver_ks.cpp | 2 +- .../source_io/module_energy/write_bands.cpp | 47 +++++++++- source/source_io/module_energy/write_bands.h | 22 ++++- source/source_io/test/write_bands_test.cpp | 89 ++++++++++++++++--- tests/integrate/validation_tools/props_mat.sh | 14 ++- 6 files changed, 168 insertions(+), 23 deletions(-) diff --git a/docs/advanced/output_files/output-specification.md b/docs/advanced/output_files/output-specification.md index 71e55026865..e3b1b128777 100644 --- a/docs/advanced/output_files/output-specification.md +++ b/docs/advanced/output_files/output-specification.md @@ -98,7 +98,7 @@ All output file naming conventions can be found in the online documentation (wit | `doss1g1.txt` | Density of states | | `eig_occ.txt` | Eigenvalues and occupations | | `mulliken.txt` | Mulliken population analysis | -| `band.txt` | Band structure | +| `band.txt` | Band structure (nspin=1/4); `bands1.txt`/`bands2.txt` when nspin=2 | | `chgs1.cube`, `chgs2.cube` | Charge density (spin 1, spin 2) | | `chg.cube` | Total charge density | | Initial charge density (out_chg=2) | | @@ -121,6 +121,21 @@ All output file naming conventions can be found in the online documentation (wit | `elfs{#}g{#}.cube` | ELF, spin {#}, geometry step {#} (nspin=2) | | `elftotg{#}.cube` | ELF, total, geometry step {#} (nspin=2) | +**Band structure files (`out_band`):** each ionic step of `band.txt`, +`bands1.txt` and `bands2.txt` is preceded by a provenance comment block, so the +k-point blocks appended by a relax/md run stay distinguishable: + +``` +# ABACUS version: v3.11.0-beta11 +# Written at 2026-10-20 15:40:45 +# IONIC STEP 1 (spin up ) + 1 0.00000000 -5.36802595 6.70427084 ... +``` + +`# IONIC STEP` is one-based and carries the collinear spin-channel label only +when `nspin = 2`. Consumers that parse the numeric columns must skip lines +starting with `#`. + ## 3. File Format Standards ### 3.1 Header Section with Comments diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index 9d12dfe07dd..01f08d8d78d 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -440,7 +440,7 @@ void ESolver_KS::after_scf(UnitCell& ucell, const int istep, const bool conv_eso istep); // 4) write band information to band.txt - ModuleIO::write_bands(*this->inp_, this->pelec->ekb, this->kv, GlobalV::ofs_running); + ModuleIO::write_bands(*this->inp_, this->pelec->ekb, this->kv, GlobalV::ofs_running, istep); } diff --git a/source/source_io/module_energy/write_bands.cpp b/source/source_io/module_energy/write_bands.cpp index e5d229f00c0..ec92f77dc99 100644 --- a/source/source_io/module_energy/write_bands.cpp +++ b/source/source_io/module_energy/write_bands.cpp @@ -9,13 +9,43 @@ #include "source_base/tool_title.h" #include "source_io/module_output/spin_tag.h" #include "source_io/module_parameter/parameter.h" // mohan add 20250911 +#include "source_main/version.h" #ifdef __MPI #include #endif +#include + +namespace +{ + +/** + * @brief build the STRU-style provenance header that precedes the k-point + * block of one ionic step in a band output file. + * + * @param istep zero-based ionic step index, printed one-based + * @param spin_tag label of the collinear spin channel, empty when nspin != 2 + * @return three comment lines: ABACUS version, local timestamp, ionic step + */ +std::string build_band_header(const int istep, const std::string& spin_tag) +{ + const std::time_t now = std::time(nullptr); + std::tm tm_local; + localtime_r(&now, &tm_local); + char time_buf[64]; + std::strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &tm_local); + return FmtCore::format("# ABACUS version: %s\n# Written at %s\n# IONIC STEP %d%s\n", + VERSION, + time_buf, + istep + 1, + spin_tag.c_str()); +} + +} // namespace + void ModuleIO::write_bands(const Input_para& inp, const ModuleBase::matrix& ekb, const K_Vectors& kv, - std::ofstream& ofs_running) + std::ofstream& ofs_running, const int istep) { // write band information to band.txt if (inp.out_band[0]) @@ -44,6 +74,7 @@ void ModuleIO::write_bands(const Input_para& inp, const ModuleBase::matrix& ekb, const std::string spin_tag = ModuleIO::make_spin_tag(is, nspin0); ofs_running << " Write eigenvalues" << spin_tag << " to file: " << eig_file << std::endl; + const std::string header = build_band_header(istep, spin_tag); const double eshift = 0.0; nscf_bands(is, eig_file, @@ -51,7 +82,8 @@ void ModuleIO::write_bands(const Input_para& inp, const ModuleBase::matrix& ekb, eshift, inp.out_band[1], // precision global_ekb, - kv); + kv, + header); } } } @@ -62,7 +94,8 @@ void ModuleIO::nscf_bands(const int& is, const double& fermie, const int& precision, const ModuleBase::matrix& ekb, - const K_Vectors& kv) + const K_Vectors& kv, + const std::string& header) { ModuleBase::TITLE("ModuleIO", "nscf_bands"); ModuleBase::timer::start("ModuleIO", "nscf_bands"); @@ -125,6 +158,10 @@ void ModuleIO::nscf_bands(const int& is, if (kv.para_k.rank_in_pool == 0 && band_group == 0) { std::ofstream ofs(eig_file.c_str(), std::ios::app); + if (ik == 0) + { + ofs << header; + } ofs << FmtCore::format("%4d", ik + 1); int width = precision + 4; std::string fmtstr = " %." + std::to_string(precision) + "f"; @@ -171,6 +208,10 @@ void ModuleIO::nscf_bands(const int& is, const int spin_k = ik + is * nks_np; if (kv.isk[spin_k] == is) { + if (ik == 0) + { + ofs << header; + } ofs << FmtCore::format("%4d", ik + 1); int width = precision + 4; std::string fmtstr = " %." + std::to_string(precision) + "f"; diff --git a/source/source_io/module_energy/write_bands.h b/source/source_io/module_energy/write_bands.h index 09c2135aff3..39b912498e5 100644 --- a/source/source_io/module_energy/write_bands.h +++ b/source/source_io/module_energy/write_bands.h @@ -11,21 +11,36 @@ namespace ModuleIO { +/** + * @brief write the eigenvalues of every spin channel to band.txt (nspin=1/4) + * or to bands1.txt/bands2.txt (nspin=2). + * + * @param inp input parameters, out_band[0] enables the output and out_band[1] + * sets the number of printed decimals + * @param ekb eigenvalues of k points and bands + * @param kv klist + * @param ofs_running running log stream + * @param istep zero-based index of the current ionic step, printed one-based + * in the per-step provenance header + */ void write_bands(const Input_para& inp, const ModuleBase::matrix& ekb, const K_Vectors& kv, - std::ofstream& ofs_running); + std::ofstream& ofs_running, + const int istep); /** * @brief calculate the band structure * * @param is spin index is = 0 or 1 - * @param out_band_dir directory to save the band structure + * @param eig_file file to save the band structure * @param nband number of bands * @param fermie fermi energy * @param precision precision of the output * @param ekb eigenvalues of k points and bands * @param kv klist + * @param header provenance header written once in front of the k-point block; + * pass an empty string to omit it */ void nscf_bands(const int& is, const std::string &eig_file, @@ -33,7 +48,8 @@ void nscf_bands(const int& is, const double& fermie, const int& precision, const ModuleBase::matrix& ekb, - const K_Vectors& kv); + const K_Vectors& kv, + const std::string& header); } #endif diff --git a/source/source_io/test/write_bands_test.cpp b/source/source_io/test/write_bands_test.cpp index 4fdcbdbb7e5..8cf6459a693 100644 --- a/source/source_io/test/write_bands_test.cpp +++ b/source/source_io/test/write_bands_test.cpp @@ -5,6 +5,7 @@ #include "source_base/module_parallel/para_world.h" #include "source_base/parallel_comm.h" #include "source_base/parallel_global.h" +#include "source_main/version.h" #include "gtest/gtest.h" @@ -227,6 +228,61 @@ class BandOutputTest : public testing::TestWithParam input_.out_band = {1, 8}; } + /// Read the next data line of a band file, skipping the provenance header. + static bool read_data_line(std::istream& stream, std::string& line) + { + while (static_cast(std::getline(stream, line))) + { + const std::size_t first = line.find_first_not_of(" \t"); + if (first != std::string::npos && line[first] == '#') + { + continue; + } + return true; + } + return false; + } + + /// Collect the comment lines of a band file in the order they were written. + std::vector comment_lines(const std::string& name) const + { + std::vector comments; + if (world_->rank() != 0) + { + return comments; + } + std::ifstream file(directory_ + name); + EXPECT_TRUE(file.is_open()) << name; + std::string line; + while (static_cast(std::getline(file, line))) + { + const std::size_t first = line.find_first_not_of(" \t"); + if (first != std::string::npos && line[first] == '#') + { + comments.push_back(line); + } + } + return comments; + } + + /// Check the three comment lines preceding k-point block number `block`. + void expect_header(const std::string& name, int block, int spin, int channels) const + { + if (world_->rank() != 0) + { + return; + } + SCOPED_TRACE(name); + const std::vector comments = comment_lines(name); + ASSERT_GE(comments.size(), static_cast(3 * block + 3)); + EXPECT_EQ(comments[3 * block], "# ABACUS version: " + std::string(VERSION)); + const std::string written = "# Written at "; + EXPECT_EQ(comments[3 * block + 1].compare(0, written.size(), written), 0) + << comments[3 * block + 1]; + const std::string tag = (channels == 2) ? (spin == 0 ? " (spin up )" : " (spin down)") : ""; + EXPECT_EQ(comments[3 * block + 2], "# IONIC STEP " + std::to_string(block + 1) + tag); + } + void expect_output(const std::string& name, int spin, int nbands, double fermi, int precision, int copies) const { @@ -244,7 +300,7 @@ class BandOutputTest : public testing::TestWithParam { for (int ik = 0; ik < 5; ++ik) { - ASSERT_TRUE(static_cast(std::getline(file, line))) << "Missing k-point " << ik; + ASSERT_TRUE(read_data_line(file, line)) << "Missing k-point " << ik; std::istringstream row(line); int index = 0; ASSERT_TRUE(static_cast(row >> index)); @@ -271,7 +327,7 @@ class BandOutputTest : public testing::TestWithParam EXPECT_FALSE(static_cast(row >> extra)) << "Unexpected column: " << extra; } } - EXPECT_FALSE(static_cast(std::getline(file, line))) << "Unexpected line: " << line; + EXPECT_FALSE(read_data_line(file, line)) << "Unexpected line: " << line; } std::string read_text(const std::string& name) const @@ -304,18 +360,21 @@ class BandOutputTest : public testing::TestWithParam { SCOPED_TRACE(distributed ? "distributed" : "replicated"); prepare(nspin, 3, distributed); - ModuleIO::write_bands(input_, ekb_, kv_, ofs_running); + ModuleIO::write_bands(input_, ekb_, kv_, ofs_running, 0); if (nspin == 2) { expect_output("bands1.txt", 0, 3, 0.0, 8, 1); expect_output("bands2.txt", 1, 3, 0.0, 8, 1); expect_missing("band.txt"); + expect_header("bands1.txt", 0, 0, 2); + expect_header("bands2.txt", 0, 1, 2); } else { expect_output("band.txt", 0, 3, 0.0, 8, 1); expect_missing("bands1.txt"); expect_missing("bands2.txt"); + expect_header("band.txt", 0, 0, 1); } // Root must finish reading before another collective call overwrites output. Parallel::barrier(*world_); @@ -326,7 +385,7 @@ class BandOutputTest : public testing::TestWithParam TEST_P(BandOutputTest, DirectSingleSpin) { prepare(1, 3, false); - ModuleIO::nscf_bands(0, directory_ + "direct.txt", 3, 0.0, 8, ekb_, kv_); + ModuleIO::nscf_bands(0, directory_ + "direct.txt", 3, 0.0, 8, ekb_, kv_, ""); expect_output("direct.txt", 0, 3, 0.0, 8, 1); if (world_->rank() == 0) { @@ -339,8 +398,8 @@ TEST_P(BandOutputTest, DirectSingleSpin) TEST_P(BandOutputTest, DirectBothSpins) { prepare(2, 3, false); - ModuleIO::nscf_bands(0, directory_ + "up.txt", 3, 0.0, 8, ekb_, kv_); - ModuleIO::nscf_bands(1, directory_ + "down.txt", 3, 0.0, 8, ekb_, kv_); + ModuleIO::nscf_bands(0, directory_ + "up.txt", 3, 0.0, 8, ekb_, kv_, ""); + ModuleIO::nscf_bands(1, directory_ + "down.txt", 3, 0.0, 8, ekb_, kv_, ""); expect_output("up.txt", 0, 3, 0.0, 8, 1); expect_output("down.txt", 1, 3, 0.0, 8, 1); } @@ -350,7 +409,7 @@ TEST_P(BandOutputTest, PrecisionAndFermiShift) prepare(1, 3, false); for (const int precision : {4, 8}) { - ModuleIO::nscf_bands(0, directory_ + "direct.txt", 3, 0.25, precision, ekb_, kv_); + ModuleIO::nscf_bands(0, directory_ + "direct.txt", 3, 0.25, precision, ekb_, kv_, ""); expect_output("direct.txt", 0, 3, 0.25, precision, 1); if (world_->rank() == 0 && precision == 4) { @@ -366,23 +425,25 @@ TEST_P(BandOutputTest, OverwriteAndAppend) prepare(1, 3, true); seed_file("band.txt", "stale output\n"); Parallel::barrier(*world_); - ModuleIO::write_bands(input_, ekb_, kv_, ofs_running); + ModuleIO::write_bands(input_, ekb_, kv_, ofs_running, 0); expect_output("band.txt", 0, 3, 0.0, 8, 1); + expect_header("band.txt", 0, 0, 1); Parallel::barrier(*world_); parameters_->set_output(directory_, true); - ModuleIO::write_bands(input_, ekb_, kv_, ofs_running); + ModuleIO::write_bands(input_, ekb_, kv_, ofs_running, 1); expect_output("band.txt", 0, 3, 0.0, 8, 2); + expect_header("band.txt", 1, 0, 1); } TEST_P(BandOutputTest, OutputDisabled) { prepare(1, 3, true); input_.out_band[0] = 0; - ModuleIO::write_bands(input_, ekb_, kv_, ofs_running); + ModuleIO::write_bands(input_, ekb_, kv_, ofs_running, 0); expect_missing("band.txt"); seed_file("band.txt", "keep this content\n"); Parallel::barrier(*world_); - ModuleIO::write_bands(input_, ekb_, kv_, ofs_running); + ModuleIO::write_bands(input_, ekb_, kv_, ofs_running, 0); if (world_->rank() == 0) { EXPECT_EQ(read_text("band.txt"), "keep this content\n"); @@ -413,9 +474,11 @@ TEST_P(BandOutputTest, EmptyBandShard) { EXPECT_EQ(ekb_.nc, 0); } - ModuleIO::write_bands(input_, ekb_, kv_, ofs_running); + ModuleIO::write_bands(input_, ekb_, kv_, ofs_running, 0); expect_output("bands1.txt", 0, 1, 0.0, 8, 1); expect_output("bands2.txt", 1, 1, 0.0, 8, 1); + expect_header("bands1.txt", 0, 0, 2); + expect_header("bands2.txt", 0, 1, 2); } TEST_P(BandOutputTest, RunningLogNamesSpinChannels) @@ -425,7 +488,7 @@ TEST_P(BandOutputTest, RunningLogNamesSpinChannels) + std::to_string(world_->rank()) + ".log"; std::ofstream log(log_name); EXPECT_TRUE(log.is_open()); - ModuleIO::write_bands(input_, ekb_, kv_, log); + ModuleIO::write_bands(input_, ekb_, kv_, log, 0); log.close(); std::ifstream in(log_name); diff --git a/tests/integrate/validation_tools/props_mat.sh b/tests/integrate/validation_tools/props_mat.sh index 5128c3dd379..bc345fee57c 100644 --- a/tests/integrate/validation_tools/props_mat.sh +++ b/tests/integrate/validation_tools/props_mat.sh @@ -106,8 +106,18 @@ fi if ! test -z "$has_band" && [ $has_band == 1 ]; then bandref=band.txt.ref bandcal=OUT.autotest/band.txt - python3 $COMPARE_SCRIPT $bandref $bandcal 8 - echo "CompareBand_pass $?" >>$props_result_file + # Every ionic step in band.txt is preceded by a provenance comment block + # ("# ABACUS version: ...", "# Written at ...", "# IONIC STEP n"), so the + # reference and the computed file are compared after those lines are + # stripped. A missing file must be reported explicitly, because process + # substitution would hand CompareFile.py an empty stream and pass silently. + if ! test -f "$bandref" || ! test -f "$bandcal"; then + echo "Error: can not find file $bandref or $bandcal" + echo "CompareBand_pass 1" >>$props_result_file + else + python3 $COMPARE_SCRIPT <(sed '/^[[:space:]]*#/d' "$bandref") <(sed '/^[[:space:]]*#/d' "$bandcal") 8 + echo "CompareBand_pass $?" >>$props_result_file + fi fi #-------------------------------- From 747646c334fb4f4d16b3fbc8693098fbd4573f67 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 16:36:16 +0800 Subject: [PATCH 28/30] fix(io): use compact spin label in band provenance header make_spin_tag pads "up" so that both collinear channels share the same width wherever they are printed next to each other in the running log. The band header instead ends up in a per-spin file (bands1.txt/bands2.txt), so the padding only leaves a trailing blank before the closing parenthesis. The header now uses " (spin up)" through a local band_spin_tag helper, while the log line keeps make_spin_tag unchanged. --- .../output_files/output-specification.md | 2 +- .../source_io/module_energy/write_bands.cpp | 27 ++++++++++++++++--- source/source_io/test/write_bands_test.cpp | 2 +- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/docs/advanced/output_files/output-specification.md b/docs/advanced/output_files/output-specification.md index e3b1b128777..99d177824dc 100644 --- a/docs/advanced/output_files/output-specification.md +++ b/docs/advanced/output_files/output-specification.md @@ -128,7 +128,7 @@ k-point blocks appended by a relax/md run stay distinguishable: ``` # ABACUS version: v3.11.0-beta11 # Written at 2026-10-20 15:40:45 -# IONIC STEP 1 (spin up ) +# IONIC STEP 1 (spin up) 1 0.00000000 -5.36802595 6.70427084 ... ``` diff --git a/source/source_io/module_energy/write_bands.cpp b/source/source_io/module_energy/write_bands.cpp index ec92f77dc99..86bdc45ecc3 100644 --- a/source/source_io/module_energy/write_bands.cpp +++ b/source/source_io/module_energy/write_bands.cpp @@ -20,6 +20,27 @@ namespace { +/** + * @brief spin-channel label for the band provenance header. + * + * Unlike ModuleIO::make_spin_tag, which pads "up" so that both channels share + * the same width wherever they are printed next to each other, the band header + * goes into a per-spin file (bands1.txt/bands2.txt), so the compact form + * " (spin up)" is used. + * + * @param is spin channel index + * @param nspin number of spins, padding is dropped unless nspin == 2 + * @return " (spin up)", " (spin down)" or an empty string + */ +std::string band_spin_tag(const int is, const int nspin) +{ + if (nspin != 2 || is < 0) + { + return ""; + } + return (is == 0) ? " (spin up)" : " (spin down)"; +} + /** * @brief build the STRU-style provenance header that precedes the k-point * block of one ionic step in a band output file. @@ -71,10 +92,10 @@ void ModuleIO::write_bands(const Input_para& inp, const ModuleBase::matrix& ekb, ss << ".txt"; const std::string eig_file = ss.str(); - const std::string spin_tag = ModuleIO::make_spin_tag(is, nspin0); - ofs_running << " Write eigenvalues" << spin_tag << " to file: " << eig_file << std::endl; + const std::string log_spin_tag = ModuleIO::make_spin_tag(is, nspin0); + ofs_running << " Write eigenvalues" << log_spin_tag << " to file: " << eig_file << std::endl; - const std::string header = build_band_header(istep, spin_tag); + const std::string header = build_band_header(istep, band_spin_tag(is, nspin0)); const double eshift = 0.0; nscf_bands(is, eig_file, diff --git a/source/source_io/test/write_bands_test.cpp b/source/source_io/test/write_bands_test.cpp index 8cf6459a693..79990fc0118 100644 --- a/source/source_io/test/write_bands_test.cpp +++ b/source/source_io/test/write_bands_test.cpp @@ -279,7 +279,7 @@ class BandOutputTest : public testing::TestWithParam const std::string written = "# Written at "; EXPECT_EQ(comments[3 * block + 1].compare(0, written.size(), written), 0) << comments[3 * block + 1]; - const std::string tag = (channels == 2) ? (spin == 0 ? " (spin up )" : " (spin down)") : ""; + const std::string tag = (channels == 2) ? (spin == 0 ? " (spin up)" : " (spin down)") : ""; EXPECT_EQ(comments[3 * block + 2], "# IONIC STEP " + std::to_string(block + 1) + tag); } From 9876bf48e6c1cb410c3c8fdc509fd0a45697b782 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 17:06:37 +0800 Subject: [PATCH 29/30] test(out_app_flag): three-mode unit test coverage for naming and append semantics Add focused unit tests exercising out_app_flag modes 0/1/2 across the filename_output primitive and all major output writers: - test_filename.cpp (new): filename_output three-mode matrix, iter/gamma/nspin - write_dmk_test.cpp: Mode0PerStepFiles / Mode1FixedNameAppend / Mode2SameNamingAsMode0 - test_write_pdos_text.cpp: Mode1FixedNameAppend / Mode2SameNamingAsMode0 - test_cal_dos.cpp: Mode1Appends (two ionic step headers) / Mode2Overwrites - test_print_cell.cpp: STRUAppend (two ATOMIC_SPECIES blocks accumulated) - test_mdcell_reader.cpp: STRUAppendWithFrameComment (# MD step N markers) - write_wfc_nao_test.cpp: OutAppFlagMode2SameNamingAsMode0 (completes 0/1/2) Also sync out_dos CLI description to document the three-mode semantics. Governance: net_delta=-9 (global dependency budget decreased). --- source/source_base/test/CMakeLists.txt | 6 + source/source_base/test/test_filename.cpp | 120 ++++++++++++++++++ .../test/test_mdcell_reader.cpp | 65 ++++++++++ source/source_cell/test/test_print_cell.cpp | 35 +++++ .../module_dm/test/write_dmk_test.cpp | 120 ++++++++++++++++++ .../module_dos/unittests/test_cal_dos.cpp | 86 +++++++++++++ .../unittests/test_write_pdos_text.cpp | 64 ++++++++++ .../module_parameter/read_inp_out.cpp | 5 +- source/source_io/test/write_wfc_nao_test.cpp | 27 ++++ 9 files changed, 526 insertions(+), 2 deletions(-) create mode 100644 source/source_base/test/test_filename.cpp diff --git a/source/source_base/test/CMakeLists.txt b/source/source_base/test/CMakeLists.txt index 16ba949d640..9964cade867 100644 --- a/source/source_base/test/CMakeLists.txt +++ b/source/source_base/test/CMakeLists.txt @@ -280,3 +280,9 @@ if(ENABLE_GOOGLEBENCH) SOURCES perf_sphbes_test.cpp ../math_sphbes.cpp ../timer.cpp ) endif() + +AddTest( + TARGET MODULE_BASE_filename + LIBS parameter + SOURCES test_filename.cpp +) diff --git a/source/source_base/test/test_filename.cpp b/source/source_base/test/test_filename.cpp new file mode 100644 index 00000000000..5bffa1fba8c --- /dev/null +++ b/source/source_base/test/test_filename.cpp @@ -0,0 +1,120 @@ +#ifndef TEST_FILENAME_CPP +#define TEST_FILENAME_CPP + +#include +#include +#include +#include "source_base/module_out/filename.h" + +// Keep local names short; do not pollute. +using ModuleIO::filename_output; + +// --------------------------------------------------------------------------- +// Fixture: minimal ik2iktot mapping for a 2-k-point pool. +// --------------------------------------------------------------------------- +class FilenameOutputTest : public ::testing::Test +{ +protected: + const std::vector ik2iktot = {0, 1}; + const int ik_local = 0; + const int nkstot = 2; + const int nspin = 2; + const std::string dir = "OUT.test/"; +}; + +// --------------------------------------------------------------------------- +// mode 0 = no append -> per-step g{N} suffix included. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, Mode0IstepAddsG) +{ + // PW wavefunction, binary, k1, spin up (ik0=0 < half_k=1 -> s1). + const std::string fn = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/2, /*out_app_flag=*/0, /*gamma_only=*/false, + /*istep=*/1, /*iter=*/-1); + EXPECT_EQ(fn, "OUT.test/wfk1s1g2_pw.dat"); +} + +// --------------------------------------------------------------------------- +// mode 1 = append -> fixed name, no g suffix even when istep >= 0. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, Mode1FixedNameNoG) +{ + const std::string fn = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/2, /*out_app_flag=*/1, /*gamma_only=*/false, + /*istep=*/1, /*iter=*/-1); + EXPECT_EQ(fn, "OUT.test/wfk1s1_pw.dat"); +} + +// --------------------------------------------------------------------------- +// mode 2 = write to subdirectory -> same naming as mode 0 (g suffix kept). +// The directory routing is handled by the caller (set_global_dir), so +// filename_output itself treats modes 0 and 2 identically. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, Mode2SameNamingAsMode0) +{ + const std::string fn = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/2, /*out_app_flag=*/2, /*gamma_only=*/false, + /*istep=*/1, /*iter=*/-1); + EXPECT_EQ(fn, "OUT.test/wfk1s1g2_pw.dat"); +} + +// --------------------------------------------------------------------------- +// istep < 0 (SCF single-point) -> no g suffix in any mode. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, SinglePointNoG) +{ + const std::string fn = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/2, /*out_app_flag=*/0, /*gamma_only=*/false, + /*istep=*/-1, /*iter=*/-1); + EXPECT_EQ(fn, "OUT.test/wfk1s1_pw.dat"); +} + +// --------------------------------------------------------------------------- +// iter >= 0 adds e{N} in modes 0/2, but is suppressed in mode 1 too. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, IterIndexRespectsAppendFlag) +{ + // mode 0: both g and e present. + const std::string fn_mode0 = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/1, /*out_app_flag=*/0, /*gamma_only=*/false, + /*istep=*/1, /*iter=*/3); + EXPECT_EQ(fn_mode0, "OUT.test/wfk1s1g2e3_pw.txt"); + + // mode 1: neither g nor e. + const std::string fn_mode1 = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/1, /*out_app_flag=*/1, /*gamma_only=*/false, + /*istep=*/1, /*iter=*/3); + EXPECT_EQ(fn_mode1, "OUT.test/wfk1s1_pw.txt"); +} + +// --------------------------------------------------------------------------- +// gamma_only suppresses the k-point label for non-hk/sk/tk properties. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, GammaOnlyNoKLabel) +{ + const std::string fn = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, nspin, nkstot, + /*out_type=*/2, /*out_app_flag=*/1, /*gamma_only=*/true, + /*istep=*/-1, /*iter=*/-1); + EXPECT_EQ(fn, "OUT.test/wfs1_pw.dat"); +} + +// --------------------------------------------------------------------------- +// nspin=4 (noncollinear SOC) -> spin block is s4, no half-k split. +// --------------------------------------------------------------------------- +TEST_F(FilenameOutputTest, NoncollinearSpin4) +{ + const std::string fn = filename_output( + dir, "wf", "pw", ik_local, ik2iktot, /*nspin=*/4, nkstot, + /*out_type=*/2, /*out_app_flag=*/2, /*gamma_only=*/false, + /*istep=*/0, /*iter=*/-1); + EXPECT_EQ(fn, "OUT.test/wfk1s4g1_pw.dat"); +} + +#endif // TEST_FILENAME_CPP diff --git a/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp b/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp index ac191c2d4cf..6a2906838b9 100644 --- a/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp +++ b/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp @@ -319,6 +319,71 @@ TEST(MDCellReaderTest, RestartStruPreservesAtomRecordsAcrossRanks) if (rank == 0) std::remove(output_file.c_str()); } +// --------------------------------------------------------------------------- +// mdcell::print_stru_file append + frame_comment. +// --------------------------------------------------------------------------- +TEST(MDCellReaderTest, STRUAppendWithFrameComment) +{ + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + std::vector owned_atoms; + owned_atoms.push_back(LocalAtom(ModuleBase::Vector3(0.0, 0.0, 0.0), + ModuleBase::Vector3(0.0, 0.0, 0.0), + ModuleBase::Vector3(0.0, 0.0, 0.0), + ModuleBase::Vector3(0.0, 0.0, 0.0), + ModuleBase::Vector3(1, 0, 1), + 1.0, + 0, + 0, + rank)); + + ModuleBase::Matrix3 lattice = make_lattice(); + MDCell mdcell; + mdcell.initialize_from_owned_atoms(lattice, + lattice.Inverse(), + 1.0, + 1.0, + 1, + owned_atoms, + std::vector{"A"}, + std::vector{1.0}, + std::vector{1}, + 0.0, + ModuleBase::world_comm_domain()); + StruMeta metadata; + metadata.species.resize(1); + const std::string output_file = "mdcell_append_test.STRU"; + + // First frame: append=false, frame comment set. + mdcell::print_stru_file(mdcell, metadata, output_file, + "# MD step 1\n\n", /*append=*/false); + // Second frame: append=true with a different comment. + mdcell::print_stru_file(mdcell, metadata, output_file, + "# MD step 2\n\n", /*append=*/true); + + if (rank == 0) + { + std::ifstream ifs(output_file.c_str()); + std::string str((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + ifs.close(); + // Both frame markers must be present. + EXPECT_THAT(str, testing::HasSubstr("# MD step 1")); + EXPECT_THAT(str, testing::HasSubstr("# MD step 2")); + // Two STRU blocks -> two ATOMIC_SPECIES markers. + int occurrences = 0; + size_t pos = 0; + while ((pos = str.find("ATOMIC_SPECIES", pos)) != std::string::npos) + { + ++occurrences; + pos += 14; + } + EXPECT_EQ(occurrences, 2); + std::remove(output_file.c_str()); + } +} + int main(int argc, char** argv) { MPI_Init(&argc, &argv); diff --git a/source/source_cell/test/test_print_cell.cpp b/source/source_cell/test/test_print_cell.cpp index 9bcf7123f33..c4ff6e08ae7 100644 --- a/source/source_cell/test/test_print_cell.cpp +++ b/source/source_cell/test/test_print_cell.cpp @@ -390,3 +390,38 @@ TEST_F(PrintCellTest, PrintTauCartesian) // remove the file remove("print_tau_Cartesian"); } + +// --------------------------------------------------------------------------- +// STRU append flag: append=true accumulates successive structures in one file. +// --------------------------------------------------------------------------- +TEST_F(PrintCellTest, STRUAppend) +{ + UcellTestPrepare utp = UcellTestLib["C1H2-Index"]; + ucell = utp.SetUcellInfo(); + const std::string fn = "STRU_append_test"; + + // First call: append=false -> create/truncate. + unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, + fn, "", 1, false, false, false, false, 0, ModuleBase::matrix(), false, + /*append=*/false); + // Second call: append=true -> accumulate into same file. + unitcell::print_stru_file(*ucell, ucell->atoms, ucell->latvec, + fn, "", 1, false, false, false, false, 0, ModuleBase::matrix(), false, + /*append=*/true); + + std::ifstream ifs(fn); + std::string str((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + ifs.close(); + // Two STRU blocks -> two ATOMIC_SPECIES markers. + int occurrences = 0; + size_t pos = 0; + while ((pos = str.find("ATOMIC_SPECIES", pos)) != std::string::npos) + { + ++occurrences; + pos += 14; + } + EXPECT_EQ(occurrences, 2); + + remove(fn.c_str()); +} diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index ab5e5bfda9b..d2d1dea45bb 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -235,6 +235,126 @@ TEST(DMKTest,WriteDMK) { }; +// --------------------------------------------------------------------------- +// out_app_flag = 0: per-step files with g suffix, overwrite each call. +// --------------------------------------------------------------------------- +TEST(DMKTest, Mode0PerStepFiles) { + UnitCell* ucell; + UcellTestPrepare utp = UcellTestLib["Si"]; + ucell = utp.SetUcellInfo(); + + int nspin = 2; + int nk = 1; + int nlocal = 20; + std::vector> dmk; + std::vector efs; + Parallel_2D pv; + init_pv(nlocal, pv); + gen_dmk(dmk, efs, nspin, nk, nlocal, pv); + + const std::string out_dir = "./"; + const int out_app_flag = 0; + std::ofstream ofs_log; + + // Two different isteps -> two differently-named files. + ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/0, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/1, out_app_flag, ofs_log); + + if (GlobalV::MY_RANK == 0) + { + // First call: g1 suffix (istep 0 -> istep+1). + std::ifstream ifs("dms1g1_nao.txt"); + EXPECT_TRUE(ifs.good()); + ifs.close(); + // Second call: g2 suffix. + ifs.open("dms1g2_nao.txt"); + EXPECT_TRUE(ifs.good()); + ifs.close(); + // Fixed-name file must NOT exist. + ifs.open("dms1_nao.txt"); + EXPECT_FALSE(ifs.good()); + remove("dms1g1_nao.txt"); + remove("dms1g2_nao.txt"); + } + delete ucell; +} + +// --------------------------------------------------------------------------- +// out_app_flag = 1: fixed filename, multiple calls append successive records. +// --------------------------------------------------------------------------- +TEST(DMKTest, Mode1FixedNameAppend) { + UnitCell* ucell; + UcellTestPrepare utp = UcellTestLib["Si"]; + ucell = utp.SetUcellInfo(); + + int nspin = 2; + int nk = 1; + int nlocal = 20; + std::vector> dmk; + std::vector efs; + Parallel_2D pv; + init_pv(nlocal, pv); + gen_dmk(dmk, efs, nspin, nk, nlocal, pv); + + const std::string out_dir = "./"; + const int out_app_flag = 1; + std::ofstream ofs_log; + + // Two ionic steps -> both write to dms1_nao.txt. + ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/0, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/1, out_app_flag, ofs_log); + + if (GlobalV::MY_RANK == 0) + { + std::ifstream ifs("dms1_nao.txt"); + std::string str((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + ifs.close(); + // Two "Ionic Step" headers present -> two appended records. + EXPECT_THAT(str, testing::HasSubstr("Ionic Step: 1")); + EXPECT_THAT(str, testing::HasSubstr("Ionic Step: 2")); + // g-suffixed file must NOT exist. + ifs.open("dms1g1_nao.txt"); + EXPECT_FALSE(ifs.good()); + remove("dms1_nao.txt"); + } + delete ucell; +} + +// --------------------------------------------------------------------------- +// out_app_flag = 2: same naming as mode 0 (g suffix kept); directory routing +// is handled by set_global_dir and is covered by io_sys_var_test. +// --------------------------------------------------------------------------- +TEST(DMKTest, Mode2SameNamingAsMode0) { + UnitCell* ucell; + UcellTestPrepare utp = UcellTestLib["Si"]; + ucell = utp.SetUcellInfo(); + + int nspin = 2; + int nk = 1; + int nlocal = 20; + std::vector> dmk; + std::vector efs; + Parallel_2D pv; + init_pv(nlocal, pv); + gen_dmk(dmk, efs, nspin, nk, nlocal, pv); + + const std::string out_dir = "./"; + const int out_app_flag = 2; + std::ofstream ofs_log; + + ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/2, out_app_flag, ofs_log); + + if (GlobalV::MY_RANK == 0) + { + std::ifstream ifs("dms1g3_nao.txt"); + EXPECT_TRUE(ifs.good()); + ifs.close(); + remove("dms1g3_nao.txt"); + } + delete ucell; +} + /* // no function in the main code calls read_dmk??? mohan note 2025-05-25 TEST(DMKTest, ReadDMK) { diff --git a/source/source_io/module_dos/unittests/test_cal_dos.cpp b/source/source_io/module_dos/unittests/test_cal_dos.cpp index 3fe5d410600..82c99219112 100644 --- a/source/source_io/module_dos/unittests/test_cal_dos.cpp +++ b/source/source_io/module_dos/unittests/test_cal_dos.cpp @@ -207,6 +207,92 @@ TEST_F(DosTest,DosW2) #endif } +// --------------------------------------------------------------------------- +// out_app_flag = 1: cal_dos appends successive records to the same file. +// --------------------------------------------------------------------------- +TEST_F(DosTest, Mode1Appends) +{ + const std::string fname = "doss1_mode1.txt"; + DosPrepare dosp(0, fname, 0.005, 18, -6, 0.07, + dos_test_data::nks, dos_test_data::nkstot, dos_test_data::nbands); + dosp.set_isk(); + dosp.set_wk(); + dosp.set_istate_info(); + + const int out_app_flag = 1; + const int bndpar = 1; + + // First call: istep=0 -> "1 # ionic step". + ModuleIO::cal_dos(dosp.is, dosp.fa, dosp.de_ev, dosp.emax_ev, dosp.emin_ev, + dosp.bcoeff, dosp.nks, dosp.nkstot, dosp.wk, dosp.isk, + dosp.nbands, dosp.ekb, dosp.wg, + /*istep=*/0, out_app_flag, bndpar); + // Second call: istep=1 -> "2 # ionic step", appended. + ModuleIO::cal_dos(dosp.is, dosp.fa, dosp.de_ev, dosp.emax_ev, dosp.emin_ev, + dosp.bcoeff, dosp.nks, dosp.nkstot, dosp.wk, dosp.isk, + dosp.nbands, dosp.ekb, dosp.wg, + /*istep=*/1, out_app_flag, bndpar); + +#ifdef __MPI + if (GlobalV::MY_RANK == 0) +#endif + { + std::ifstream ifs(fname); + std::string str((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + ifs.close(); + // Two ionic step headers present. + EXPECT_THAT(str, testing::HasSubstr("1 # ionic step")); + EXPECT_THAT(str, testing::HasSubstr("2 # ionic step")); + remove(fname.c_str()); + } +} + +// --------------------------------------------------------------------------- +// out_app_flag = 2: same overwrite behaviour as mode 0 (file is truncated each +// call; naming is handled by the caller write_dos_* which adds g{N}). +// --------------------------------------------------------------------------- +TEST_F(DosTest, Mode2Overwrites) +{ + const std::string fname = "doss1_mode2.txt"; + DosPrepare dosp(0, fname, 0.005, 18, -6, 0.07, + dos_test_data::nks, dos_test_data::nkstot, dos_test_data::nbands); + dosp.set_isk(); + dosp.set_wk(); + dosp.set_istate_info(); + + const int out_app_flag = 2; + const int bndpar = 1; + + // First call writes istep 1, second overwrites with istep 2. + ModuleIO::cal_dos(dosp.is, dosp.fa, dosp.de_ev, dosp.emax_ev, dosp.emin_ev, + dosp.bcoeff, dosp.nks, dosp.nkstot, dosp.wk, dosp.isk, + dosp.nbands, dosp.ekb, dosp.wg, + /*istep=*/0, out_app_flag, bndpar); + ModuleIO::cal_dos(dosp.is, dosp.fa, dosp.de_ev, dosp.emax_ev, dosp.emin_ev, + dosp.bcoeff, dosp.nks, dosp.nkstot, dosp.wk, dosp.isk, + dosp.nbands, dosp.ekb, dosp.wg, + /*istep=*/1, out_app_flag, bndpar); + +#ifdef __MPI + if (GlobalV::MY_RANK == 0) +#endif + { + std::ifstream ifs(fname); + std::string str((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + ifs.close(); + // Only the last ionic step header must be present. + EXPECT_THAT(str, testing::HasSubstr("2 # ionic step")); + // A simple count check: "1 # ionic step" must appear only inside + // the DOS grid lines as a value, not as a header. Check that the + // header for step 1 is absent by verifying it is NOT found as a + // standalone header line. + EXPECT_THAT(str, testing::Not(testing::HasSubstr("\n1 # ionic step"))); + remove(fname.c_str()); + } +} + #ifdef __MPI int main(int argc, char **argv) { diff --git a/source/source_io/module_dos/unittests/test_write_pdos_text.cpp b/source/source_io/module_dos/unittests/test_write_pdos_text.cpp index 52571d2888f..69a93c8b92b 100644 --- a/source/source_io/module_dos/unittests/test_write_pdos_text.cpp +++ b/source/source_io/module_dos/unittests/test_write_pdos_text.cpp @@ -177,3 +177,67 @@ TEST_F(WritePdosTextTest, SpinPolarized) GlobalV::ofs_running.close(); remove("test_write_pdos_text.log"); } + +// --------------------------------------------------------------------------- +// out_app_flag = 1: fixed-name files, successive calls append records. +// --------------------------------------------------------------------------- +TEST_F(WritePdosTextTest, Mode1FixedNameAppend) +{ + GlobalV::ofs_running.open("test_write_pdos_text.log"); + + const int nspin = 1; + const int nlocal = 4; + const int npoints = 2; + const double emin = 0.0; + const double dos_edelta_ev = 1.0; + const int out_app_flag = 1; + + ModuleBase::matrix pdos(nlocal, npoints); + pdos.zero_out(); + + // First call: istep=0, mode 1 -> fixed name pdoss1.txt. + ModuleIO::write_pdos_text(*ucell, &pdos, nspin, nlocal, npoints, + emin, dos_edelta_ev, "./", /*istep=*/0, out_app_flag); + // Second call: istep=1, mode 1 -> append to same file. + ModuleIO::write_pdos_text(*ucell, &pdos, nspin, nlocal, npoints, + emin, dos_edelta_ev, "./", /*istep=*/1, out_app_flag); + + const std::string str = read_file("./pdoss1.txt"); + // Two istep headers present -> two appended records. + EXPECT_THAT(str, testing::HasSubstr("# istep: 1")); + EXPECT_THAT(str, testing::HasSubstr("# istep: 2")); + // g-suffixed file must NOT exist. + std::ifstream ifs("./pdoss1g1.txt"); + EXPECT_FALSE(ifs.good()); + + remove("./pdoss1.txt"); + GlobalV::ofs_running.close(); + remove("test_write_pdos_text.log"); +} + +// --------------------------------------------------------------------------- +// out_app_flag = 2: same naming as mode 0 (g suffix kept). +// --------------------------------------------------------------------------- +TEST_F(WritePdosTextTest, Mode2SameNamingAsMode0) +{ + GlobalV::ofs_running.open("test_write_pdos_text.log"); + + const int nspin = 1; + const int nlocal = 4; + const int npoints = 2; + const double emin = 0.0; + const double dos_edelta_ev = 1.0; + const int out_app_flag = 2; + ModuleBase::matrix pdos(nlocal, npoints); + pdos.zero_out(); + + ModuleIO::write_pdos_text(*ucell, &pdos, nspin, nlocal, npoints, + emin, dos_edelta_ev, "./", 3, out_app_flag); + + const std::string str = read_file("./pdoss1g4.txt"); + EXPECT_THAT(str, testing::HasSubstr("# istep: 4")); + + remove("./pdoss1g4.txt"); + GlobalV::ofs_running.close(); + remove("test_write_pdos_text.log"); +} diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index f869afb8f4f..c05e113dcc6 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -341,8 +341,9 @@ Also controled by out_freq_ion and out_app_flag. item.description = R"(Whether to output the density of states (DOS) and projected density of states (PDOS). For more information, refer to the dos.md. * 0: no output * 1: output the density of states (DOS) and projected density of states (PDOS, LCAO only) - * nspin=1 or 4: doss1g{geom}.txt and pdoss1g{geom}.txt (PDOS is LCAO only), where geom is the geometry index when cell changes or ions move. PW and LCAO calculations use the same naming. - * nspin=2: doss1/doss2 and pdoss1/pdoss2 files for two spin channels. + * With out_app_flag = 0 or 2: per-step files are named `doss{s}g{geom}.txt` and `pdoss{s}g{geom}.txt` where `{geom}` is the one-based geometry index (e.g. `doss1g3.txt` for the third ionic step). + * With out_app_flag = 1: fixed-name files (`doss{s}.txt`, `pdoss{s}.txt`) are reused; the first written step truncates them and later steps append one record per step (prefixed with a `# ionic step N` header, one-based). + * nspin=1 or 4: one spin channel (`s1`). nspin=2: two channels (`s1`, `s2`). PW and LCAO use the same naming. Note: values 2 and 3 are no longer supported. Setting out_dos to 2 or 3 will raise an error.)"; item.default_value = "0"; item.unit = ""; diff --git a/source/source_io/test/write_wfc_nao_test.cpp b/source/source_io/test/write_wfc_nao_test.cpp index 8d7bc6b6a48..e383ab9e1c8 100644 --- a/source/source_io/test/write_wfc_nao_test.cpp +++ b/source/source_io/test/write_wfc_nao_test.cpp @@ -102,6 +102,33 @@ TEST(GenWfcLcaoFnameTest, OutTypeInvalid) EXPECT_EQ(result, expected_output); } +// --------------------------------------------------------------------------- +// out_app_flag = 2 (write to subdirectory) keeps the g{N} suffix, identical +// to mode 0 at the filename level; directory routing is handled by the caller. +// --------------------------------------------------------------------------- +TEST(GenWfcLcaoFnameTest, OutAppFlagMode2SameNamingAsMode0) +{ + const std::string directory = ""; + const std::string property = "wf"; + const std::string basis = "nao"; + const int ik = 1; + const std::vector ik2iktot = {0, 1}; + const int nspin = 2; + const int nkstot = 2; + const int out_type = 2; + const int out_app_flag = 2; + const bool gamma_only = true; + const int istep = 2; + + std::string result = ModuleIO::filename_output( + directory, property, basis, ik, ik2iktot, nspin, + nkstot, out_type, out_app_flag, gamma_only, istep); + + // Same g-suffixed naming as mode 0. + std::string expected_output = "wfs2g3_nao.dat"; + EXPECT_EQ(result, expected_output); +} + template void read_bin(const std::string& name, std::vector& data) { From 40c8bcdfaffacb6cd3ce94867f35f305c866b66b Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Tue, 20 Oct 2026 17:28:17 +0800 Subject: [PATCH 30/30] fix(test): add missing link libs and K_Vectors/precision args - CMakeLists: MODULE_BASE_filename needs both `base` (filename.cpp) and `device` (kernel ops referenced by base) in its LIBS - test_mdcell_reader: include gmock/gmock.h for HasSubstr matcher - write_dmk_test: new three-mode tests were missing the K_Vectors construction and the precision parameter (third positional arg); sync with the WriteDMK precedent in the same file Build verified by developer (make -j 30). --- source/source_base/test/CMakeLists.txt | 2 +- .../test/test_mdcell_reader.cpp | 1 + .../module_dm/test/write_dmk_test.cpp | 52 +++++++++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/source/source_base/test/CMakeLists.txt b/source/source_base/test/CMakeLists.txt index 9964cade867..7a48043bef6 100644 --- a/source/source_base/test/CMakeLists.txt +++ b/source/source_base/test/CMakeLists.txt @@ -283,6 +283,6 @@ endif() AddTest( TARGET MODULE_BASE_filename - LIBS parameter + LIBS parameter base device SOURCES test_filename.cpp ) diff --git a/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp b/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp index 6a2906838b9..e23707c7aab 100644 --- a/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp +++ b/source/source_cell/module_neighlist/test/test_mdcell_reader.cpp @@ -1,4 +1,5 @@ #include "source_cell/domain_decomposition.h" +#include #include #include "source_cell/mdcell_reader.h" diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index d2d1dea45bb..15dce489046 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -252,13 +252,27 @@ TEST(DMKTest, Mode0PerStepFiles) { init_pv(nlocal, pv); gen_dmk(dmk, efs, nspin, nk, nlocal, pv); + K_Vectors kv; + kv.set_nkstot(1); + kv.set_nkstot_nospin(1); + kv.set_nks(1); + kv.set_spin_mult(2); + kv.kvec_c.resize(1); + kv.kvec_c[0].x = 0.0; + kv.kvec_c[0].y = 0.0; + kv.kvec_c[0].z = 0.0; + kv.wk.resize(1); + kv.wk[0] = 1.0; + kv.isk.resize(1); + kv.isk[0] = 0; + const std::string out_dir = "./"; const int out_app_flag = 0; std::ofstream ofs_log; // Two different isteps -> two differently-named files. - ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/0, out_app_flag, ofs_log); - ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/1, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, kv, /*precision=*/3, efs, ucell, pv, out_dir, /*istep=*/0, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, kv, /*precision=*/3, efs, ucell, pv, out_dir, /*istep=*/1, out_app_flag, ofs_log); if (GlobalV::MY_RANK == 0) { @@ -296,13 +310,27 @@ TEST(DMKTest, Mode1FixedNameAppend) { init_pv(nlocal, pv); gen_dmk(dmk, efs, nspin, nk, nlocal, pv); + K_Vectors kv; + kv.set_nkstot(1); + kv.set_nkstot_nospin(1); + kv.set_nks(1); + kv.set_spin_mult(2); + kv.kvec_c.resize(1); + kv.kvec_c[0].x = 0.0; + kv.kvec_c[0].y = 0.0; + kv.kvec_c[0].z = 0.0; + kv.wk.resize(1); + kv.wk[0] = 1.0; + kv.isk.resize(1); + kv.isk[0] = 0; + const std::string out_dir = "./"; const int out_app_flag = 1; std::ofstream ofs_log; // Two ionic steps -> both write to dms1_nao.txt. - ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/0, out_app_flag, ofs_log); - ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/1, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, kv, /*precision=*/3, efs, ucell, pv, out_dir, /*istep=*/0, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, kv, /*precision=*/3, efs, ucell, pv, out_dir, /*istep=*/1, out_app_flag, ofs_log); if (GlobalV::MY_RANK == 0) { @@ -339,11 +367,25 @@ TEST(DMKTest, Mode2SameNamingAsMode0) { init_pv(nlocal, pv); gen_dmk(dmk, efs, nspin, nk, nlocal, pv); + K_Vectors kv; + kv.set_nkstot(1); + kv.set_nkstot_nospin(1); + kv.set_nks(1); + kv.set_spin_mult(2); + kv.kvec_c.resize(1); + kv.kvec_c[0].x = 0.0; + kv.kvec_c[0].y = 0.0; + kv.kvec_c[0].z = 0.0; + kv.wk.resize(1); + kv.wk[0] = 1.0; + kv.isk.resize(1); + kv.isk[0] = 0; + const std::string out_dir = "./"; const int out_app_flag = 2; std::ofstream ofs_log; - ModuleIO::write_dmk(dmk, 1, efs, ucell, pv, out_dir, /*istep=*/2, out_app_flag, ofs_log); + ModuleIO::write_dmk(dmk, kv, /*precision=*/3, efs, ucell, pv, out_dir, /*istep=*/2, out_app_flag, ofs_log); if (GlobalV::MY_RANK == 0) {