Repository navigation
Fix part of 6608: Make IO output self-describing: running-log messages, cube data_desc, and ofs_running parameterization - #8068
Conversation
The TB2J interface (issue deepmodeling#6608) reads the Fermi energy from ABACUS output to set the upper bound of the Green's-function integral, but the "EFERMI = ... eV" line was removed from output_efermi on 2025-06-22 and the CSR header of H(R) carried no Fermi information either. - output_log: output_efermi now takes elecstate::Efermi and prints " E_Fermi = <ef> eV" or " E_Fermi = <up> <down> eV # two fermi energies (up, down)" when two_efermi is set. - hsr_writer: write_hcontainer_csr accepts an extra efermi_eV argument and appends ", E_Fermi = <value> eV" to the "# spin index" header line only when label == "H". write_hsr threads elecstate::Efermi through and converts each spin channel via Efermi::get_efval(ispin). - esolver_fp / ctrl_scf_lcao: pass pelec->eferm down to the writers. - write_dh / hterm_writer: pass 0.0 for the Fermi argument since dH/dR and other derived terms are not Hamiltonians and must not carry the Fermi annotation. - unit tests: migrate OutputEfermiTest to the Efermi signature, add TestSingleFermi / TestTwoFermi content checks; extend hsr_writer unittests with per-channel Fermi header coverage and an "S(R) has no E_Fermi" assertion; link fp_energy.cpp into MODULE_IO_hsr_writer_test. - integration reference: refresh scf_out_hsr/hrs1_nao.csr.ref with the new header line; remaining hrs*.csr.ref files will follow after their reference Fermi values are confirmed.
- test_hsr_writer: expect setprecision(6) significant-digit output (5.4321 / 3.2109) instead of fixed 6-decimal strings; write the two spin channels to separate files since istep=0 opens in truncate mode and the second write was overwriting the first. - outputlog_test: recompute expected E_Fermi values with the actual ModuleBase::Ry_to_eV = 13.605698 constant instead of 13.605693122994. Verified: ctest -R "MODULE_IO_hsr_writer_test|MODULE_IO_output_log_test" passes 3/3.
Report each matrix file with a consistent "Write <X> matrix in NAO basis to file:" line in running log so users can see the write action: H(R), S(R), T(R), r(R), and dH/dR components now all follow the eig/occ message style. Drop the redundant "#Print out dH/dR components#" banner. Pass ofs_running into write_hsr, out_lat_r, and out_rR instead of reading GlobalV inside the IO layer.
Print "Write H(R) (<term> term) matrix in NAO basis to file: <fname>" for the individual Hamiltonian terms (kinetic/nonlocal/local/hartree/ xc/exx) when out_mat_h_t/vnl/vl/vh/vxc/exx writes their H(R) CSR files. Thread ofs_running through WriteHParams and gather_and_write instead of reading GlobalV inside the IO layer.
Print "Write dH/dR (<term> term) matrix in NAO basis to file: <fname>" for each dH/dR component CSR file written by out_mat_dh and out_mat_dh_t/vnl/vl/vh/vxc/exx (including the Pulay variants and the total dH sum). Thread ofs_running through WriteDHParams and pass a term name into write_dh_perI instead of reading GlobalV in the IO layer.
Add "Write <X> matrix in NAO basis to file" running-log lines for the matrix outputs that previously stayed silent: DM(R) (out_dmr), DM(k) (out_dmk, replacing a commented-out cout), dS/dR (out_mat_ds, and fix the label that hardcoded "dH/dR" for dS files), T(k) (out_mat_tk), L(R) (out_mat_l, replacing the large banner), and Vxc(R) (out_mat_xc2). Mark spin-polarized channels as "(spin up )"/"(spin down)" padded to equal width before the word "matrix" so consecutive lines stay aligned. Thread ofs_running through the IO functions (write_dmr, write_dmk, write_Vxc_R) instead of reading GlobalV inside the IO layer.
…sages Move the generic "Write data to file" message out of the low-level ModuleIO::write_cube (shared by all volumetric outputs) up into write_vdata_palgrid, which knows the spin component. Add a data_desc parameter (default "data") so the charge/potential call sites can pass "charge density" and "potential", and annotate spin-polarized files with "(spin up )"/"(spin down)". Do the same for out_band: thread nspin0 into nscf_bands so bands1/bands2 are labeled by spin channel. eig_occ.txt holds all spins in one file and stays unlabeled.
Cache the global running-log stream in a local std::ofstream& at the top of ctrl_scf_lcao and pass that reference onward, instead of naming GlobalV::ofs_running at each call site. This drops the repeated global references (13 -> 1) and brings the PR's global-dependency budget back below zero.
# Conflicts: # source/source_io/module_dhs/write_dh.h # source/source_io/module_dhs/write_dh_terms.cpp
Add the ', E_Fermi = 10.3798 eV' annotation to the '# spin index' line of hrs1_nao.csr.ref and hrs2_nao.csr.ref so the reference matches the output of write_hcontainer_csr. The Fermi value (10.3798 eV) is taken from the last E_Fermi row of the energy table in running_scf.log.
… write_hcontainer_csr
The previous implementation keyed the Fermi-energy annotation on the
string label == "H" and used 0.0 as a sentinel for 'no Fermi value'.
That silently emitted 'E_Fermi = 0 eV' when a caller wrote an H(R)
file without a Fermi value available (e.g. the gamma-folded test),
which looks like a real value.
Add an explicit 'const bool has_efermi' parameter to write_hcontainer_csr.
The spin-index header line now appends ', E_Fermi = <value> eV' iff
has_efermi is true, independent of label. Update all call sites:
- write_hsr: H(R) -> true, S(R) -> false
- write_h_term (T/V^NL/V^L/V^H/V^XC): false
- write_dh_perI (dH/dR components): false
- unittests: pass true/false explicitly
Add a Not(HasSubstr("E_Fermi")) assertion to GammaFoldedHeaderKeepsCsrReadable
to lock in that label="H" + has_efermi=false produces no Fermi line.
- cube_io.h: remove `data_desc = "data"` default; pass explicit descriptive strings at all 22 call sites (charge density, partial charge, effective/electrostatic potential, kinetic energy density, electron localization function, local DOS, wave function norm/real/imag, exciton density). - write_bands.h: remove `nspin0 = 1` default; pass nspin0=1 explicitly at 4 test call sites in write_bands_test.cpp. - Normalize cube filenames to lowercase with consistent short prefixes: SPIN1_CHG.cube -> chgs1.cube (esolver_dm2rho) LDOS_<en>eV.cube -> ldos_<en>ev.cube (cal_ldos + stm.py + catch_properties.sh) Exciton_avg_<type>_state<n>_spin<s>.cube -> exc_<type>_st<n+1>_s<s+1>.cube (exciton_plotter; indices now start from 1) - Rename test reference: LDOS.cube.ref -> ldos.cube.ref (git mv). - Update result.ref and README to match new filenames. Verification: make -j 30 passed; ctest 7/7 passed (rho_io, write_bands, write_bands_parallel, hsr_writer_test, hsr_writer_test_parallel, dhs_sparse_writer_test, output_mat_sparse_test); code_quality_score average 74.9 (pass line 60), 3 files below 60 are pre-existing debt (tab_indentation, global_dependency, too_many_parameters in cal_ldos/ctrl_output_fp/exciton_plotter).
- chg_init.cpp: read_kin_file now probes and reads tau.cube (nspin==1)
/ taus1.cube (nspin>1) instead of the legacy SPIN1_TAU.cube /
SPIN<n>_TAU.cube. Aligns with the charge read path (chg.cube /
chgs1.cube) in the same file.
- test_chg_extra.cpp: remove dead `std::remove("./support/OLD2_SPIN1_CHG.cube")`
in ExtrapolateChargeCase4; no code path generates that file.
Verification: make -j 30 passed; ctest 8/8 passed (MODULE_CHARGE_extra +
the 7 IO tests from the prior commit); code_quality_score chg_init.cpp
64 (pass), test_chg_extra.cpp 57 (pre-existing debt: tab_indentation,
uppercase_constant, global_dependency).
Critsium-xy
left a comment
There was a problem hiding this comment.
Follow-up review on fa75532.
Verification: ran agent_governance_check.py --base 4c69130 --head fa75532 (0 errors, 46 warnings, GlobalV net delta -5) and code_quality_score.py on the 66 changed C++ files; static cross-check of every write_vdata_palgrid call site, every hrs*.csr.ref, and renamed-filename references across tests/, tools/ and docs/. Not compiled and no tests executed locally, so the make -j 30 / ctest results in the body are unverified here.
Address remaining review comments on PR deepmodeling#8068: - Let callers own the spin tag via make_data_desc() in cube_io.h; the removed write_cube.cpp inference mislabeled is=-1/1-based/summed calls as "(spin down)" or "(spin up)". nspin=4 magnetic channels now log magnetization density mx/my/mz (chgs), effective magnetic field bx/by/bz (pots, holding B_xc) instead of bare "charge density" / "effective potential" for all four channels. - Drop the 5 pre-write cube log lines duplicated by write_vdata_palgrid. - Sync hs_matrix.md CSR header example with the E_Fermi annotation. - Rename the conditional-slice .dat to the 1-based exc_cond_ style so one run no longer mixes Exciton_cond_..._state<N> (0-based) with exc_..._st<N+1> names for the same state. - Strip the 6-digit E_Fermi header annotation before the 1e-8 CSR comparison in catch_properties.sh to avoid platform-dependent rounding failures; matrix tokens still compare at 1e-8.
# Conflicts: # source/source_io/module_ctrl/ctrl_output_fp.cpp
ReadFile() pre-checked inputs with os.path.isfile(), which returns False for the /dev/fd/<n> pipes produced by bash process substitution <(...), so the CSR comparisons in catch_properties.sh (added in 23d0ee4 to strip the E_Fermi header annotation) aborted with "can not find file". Open the path directly and catch OSError instead; missing files still report the same error and exit code.
# Conflicts: # tests/integrate/tools/catch_properties.sh
- Add compare_hr_csr helper: a missing hrs*/ref file now fails explicitly instead of passing via an empty process-substitution stream. - Compare the E_Fermi header values separately at 1e-3 eV so the updated .ref annotations are actually verified. - Compare sr_nao.csr directly; S(R) carries no E_Fermi annotation. - Drop unrelated commented-out npz entries from CASES_CPU/GPU.txt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- dm2rho now writes chg.cube for nspin=1 (chgs<n>.cube otherwise), matching ctrl_output_fp and the init_chg=file reader. - read_kin_file falls back to legacy SPIN<n>_TAU.cube names with a warning, and warns when no tau cube is found before the Thomas-Fermi fallback. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
plot_istate is documented as 0-based and write_slice_data prints the 0-based index in the .dat header, so the st<n> tag in exc_* file names now uses the same index instead of istate+1. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l outputs - ctrl_iter_pw / ctrl_runner_pw: pass the ofs_running parameter to write_wfc_pw, DONE and the ML-KEDF set_para instead of GlobalV. - ctrl_output_fp: write_dipole uses the ofs_running parameter. - ctrl_scf_lcao: take ofs_running as an explicit parameter instead of re-binding GlobalV::ofs_running locally; the caller passes the stream. - Realign the ofs_running parameter in ctrl_output_pw instantiations. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…_bands - Add ModuleIO::make_spin_tag (module_output/spin_tag.h) and use it in make_data_desc and the DM(k), DM(R), H(R), H(R)-term and Vxc(R) writers instead of seven copies of the same nested ternary. - Move the "Write eigenvalues" log line into write_bands, which owns nspin0, so nscf_bands keeps its upstream signature (no extra nspin0 parameter). - Test the nspin=2 running-log labels in write_bands_test and add test_spin_tag.cpp for the helper. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
For nspin=2 the PW wavefunction cubes, dS/dR and dH/dR sparse files, and dH/dR term files now log "(spin up )"/"(spin down)" like the LCAO wavefunction and H(R) writers, using ModuleIO::make_spin_tag. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…bels make_spin_tag already returns an empty suffix for nspin=1 and nspin=4, so get_wf_lcao builds the labels directly instead of duplicating defaults behind if (nspin_ == 2) and passing an unused "magnetization" base. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r read Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This reverts commit aa26f65. The PR intentionally makes exciton file-name indices 1-based (st<n+1>), matching the other normalized cube names, so restore the author's naming. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
dm2rho wrote only chgs1.cube for nspin=4. The init_chg=file reader treats chgs1.cube as "cube files present" and then reads chgs2..4.cube, so a restart from a dm2rho directory aborted in read_vdata_palgrid. Write every channel, matching ctrl_output_fp. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The caller ctrl_output_fp already receives the running-log stream, so pass it through instead of adding two GlobalV::ofs_running reads. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@mohanchen I edited the PR description so it matches the review follow-up commits I pushed to this branch ( What changed in the description:
The exciton file names keep your 1-based |
…d::vector deepmodeling#8068 added a test that assigned `new int[1]` to ucell.iat2it, which no longer compiles now that UnitCell owns iat2it as std::vector<int>. Use resize() as the other test in this file already does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ing reference members (#8080) * Refactor: remove the UnitCell Statistics layer and flattening reference members UnitCell stored its atom-count and index-map data in a nested `Statistics` struct and re-exported every field as a reference member (`int& nat = st.nat`, `int*& iat2it = st.iat2it`, ...). This created dual-view aliasing, an invisible raw-pointer ownership rule (fixtures had to `new[]` but never `delete[]`, and several unit tests double-freed as a result), and a latent shallow-copy double-free since the implicit copy constructor remained available while copy assignment was silently deleted by the reference members. Changes: - Repoint the only consumers of the `Statistics` type: module_symmetry and module_exx_symmetry interfaces now take `const UnitCell&` instead of `const Statistics&`; all call sites pass `ucell` (or `*ucell`). - Move the nine fields into UnitCell as direct members under the existing top-level names, so `ucell.nat`-style call sites are unchanged. Delete the `st` member, the reference members, and the `Statistics` struct. - Convert iat2it/iat2ia/iwt2iat/iwt2iw from raw int* to std::vector<int>; migrate the ~100 test/support allocation sites from `new int[]` to resize()/assign(). Production sites (unitcell.cpp, cal_wfc.cpp) and the two force_pw.cpp device-upload call sites use .data(). - Delete UnitCell copy constructor/assignment and default the move operations, so accidental copies fail at compile time. - Update AGENTS.md: replace the obsolete fixture note with the new ownership/vector rule. Verification: - Full build succeeds (no compile errors). - ctest -R "unitcell|CELL_SYMMETRY|symmetry|hcontainer|dftu|operator_lcao| module_dm|pdos|hsr|restart_exx|gint|relax|esolver": 33/33 passed. - agent_governance_check: no issues. No INPUT parameter or user-facing behavior change; docs/parameters.yaml unaffected. The Lattice lat reference-member block is intentionally out of scope and can follow the same recipe in a later PR. * Fix GPU build: use .data() for iat2it cudaMemcpy in gint_gpu_vars The Statistics refactor converted UnitCell::iat2it from int* to std::vector<int>. The CUDA-only translation unit gint_gpu_vars.cpp was not compiled by the local CPU build, so this call site was missed: cudaMemcpy expects a const void* source but received the vector object. Pass ucell.iat2it.data() instead, matching the force_pw.cpp device-upload sites. * Delete UnitCell move operations to avoid double free The defaulted move constructor shallow-copied the owning raw pointer 'atoms' together with 'set_atom_flag' without resetting the source, so both the moved-to and moved-from objects ran delete[] on the same Atom array. The defaulted move assignment was implicitly deleted because the reference aliases into 'lat' (Coordinate, a1, latvec, ...) cannot be re-bound, triggering -Wdefaulted-function-deleted. Explicitly delete both move operations so any accidental move fails at compile time; correct move semantics require an owning container for 'atoms' and removal of the reference aliases in later refactoring steps. * Add compile-time guard for UnitCell non-copyable/non-movable contract Add static_asserts on std::is_copy/move_constructible/assignable so that re-enabling either operation during future UnitCell refactoring fails the translation unit at compile time instead of reintroducing the double free and dangling 'lat' reference aliases. * update AGENTS.md * Decouple Symmetry::analy_sys from UnitCell Replace the const UnitCell& parameter of analy_sys (and its three private helpers is_all_movable, analyze_magnetic_group, analyze_magnetic_group_nspin4) with only the index data they actually read: nat, ntype, iat2it, iat2ia, itia2iat. This breaks the UnitCell <-> Symmetry type-level dependency cycle: symmetry.h no longer names UnitCell at all, and the three implementation files drop their unitcell.h includes. The two other classes in the module that still need UnitCell (Irreducible_Sector, Symmetry_rotation_k) carry their own forward declaration now. All production and test call sites pass the five data members explicitly; behavior is unchanged. * Decouple Irred_Sector and Symmetry_rotation_k from UnitCell Replace the const UnitCell& parameters of find_irred_sector, cal_return_lattice_all, gen_symm_bvk (Irred_Sector) and cal_Ms / contruct_2d_rot_mat_ao (Symmetry_rotation_k) with the data they actually read: nat, ntype, the iat2it/iat2ia maps, and explicit symm/atoms/lat/latvec/lmax arguments. This removes the possibility of passing mismatched atoms/lat alongside one cell's index maps and makes both headers free of any UnitCell name; the three implementation files drop their unitcell.h includes. Drop the unused get_aRb_direct helpers (no callers anywhere in the tree). Rename for brevity: Irreducible_Sector -> Irred_Sector, irreducible_sector.{h,cpp} -> irred_sec.{h,cpp} (matching the existing irred_sec_bvk.cpp and the irs_ member), find/get_irreducible_sector -> find/get_irred_sector, and gen_symmetry_BvK -> gen_symm_bvk. Comments, log strings and the output file name keep the standard "BvK" spelling. All three production call sites (DFTU x2, RDMFT) and the EXX info printer are updated; behavior is unchanged. * Decouple EXX Symmetry_rotation from UnitCell and drop dead test helpers The EXX/RPA/RDMFT rotation templates only consume the atom index maps, so pass iat2it/iat2ia explicitly instead of const UnitCell&, and remove the unitcell.h include from symm_rotation.h (forward-declare UnitCell for the non-template print_symrot_info_k). TUs that dereference UnitCell now include unitcell.h directly. Delete unreachable helpers: test_HR_rotation (both overloads), test_Cs_rotation, get_Rs_from_adjacent_list, get_Rs_from_BvK. Also fix the three missed .hpp call sites of the Irred_Sector rename (exx_lri_interface.hpp, exx_lri.hpp, rpa_lri.hpp). Verified: make -j 30 builds abacus_max_para with 0 warnings; MODULE_RI_EXX_SYMMETRY_rotation test target also builds. * Point integration tests to local build path Set the ABACUS executable in Autotest.sh and general_info to the local build_max_para_test/abacus_max_para binary for local test runs. * Fix Intel Makefile build after irred_sec rename Makefile.Objects still listed irreducible_sector.o, which no longer exists after renaming irreducible_sector.{h,cpp} to irred_sec.{h,cpp}; update it to irred_sec.o so the mpiicpx Makefile build links again. * Fix iat2it/iat2ia allocation in relax_sync unit test The upstream merge brought in a test that assigned new int[nat] to ucell.iat2it/iat2ia, but these are std::vector<int> members on this branch. Use resize() instead, matching the other fixtures in this file and removing the leaked allocation. Verified: MODULE_RELAX_relax_sync_test target builds with 0 errors. * Revert general_info EXEC to 'abacus' The local absolute build path broke CUDA CI jobs that do not have this directory; restore the upstream default of resolving abacus from PATH. * Revert Autotest.sh executable to 'abacus' Complete the partial revert in b6be72d: the hardcoded local path build_max_para_test/abacus_max_para was still set in Autotest.sh, so CUDA CI jobs (which lack that local CPU build) failed to launch every integration test case. Restore the upstream default and resolve the executable via PATH, ABACUS_EXE, or the -a option for local runs. * Fix stale Statistics/Irreducible_Sector uses after merging #8099 The previous merge of upstream/develop brought in #8099, whose code still used the pre-refactor API and broke every CI build: - symm_rotation_k.cpp: reset_symmetry() assigned Irreducible_Sector(), renamed to Irred_Sector on this branch. - symm_test_analysis.cpp and test_symm_rotation.cpp: replace ucell.st, find_irreducible_sector() and cal_Ms(kv, cell, ...) with the new explicit nat/ntype/iat2it/iat2ia/itia2iat signatures, and assign the index maps as std::vector instead of new int[]. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix test_hsr_writer after merging #8068: iat2it is now std::vector #8068 added a test that assigned `new int[1]` to ucell.iat2it, which no longer compiles now that UnitCell owns iat2it as std::vector<int>. Use resize() as the other test in this file already does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn> Co-authored-by: Critsium <tsfxwbbzxy@163.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix part of #6608 fix #8096
Summary
This PR improves the readability and consistency of ABACUS IO output (running log and
.cubefiles) and removes a class of default-argument debt (Rule 5) from the IO layer. The work is split into three themes that build on each other.1. Running-log messages: label what is being written
The running log previously contained many bare
Write data to file: ...lines that did not say what data was written. This PR adds descriptive labels and spin tags so each line is self-describing:out_mat_h,out_mat_dh,out_mat_*) now reportWrite H matrix ...,Write dH matrix ..., etc., with per-spin/channel tags where applicable.data_descstring at every call site (charge density, partial charge, effective/electrostatic potential, kinetic energy density, electron localization function, local DOS, wave function norm/real/imag, exciton density). The spin tag is owned by the caller throughmake_data_desc(cube_io.h) instead of being inferred fromisinsidewrite_vdata_palgrid, so spin-summed (is = -1) and 1-based-index callers are labeled correctly. nspin=4 magnetic channels are labeledmagnetization density m{x,y,z}(rho[1..3]) andeffective magnetic field b{x,y,z}(the B_xc components of the potential) instead of bare "charge density" / "effective potential" for all four channels.(spin up )/(spin down)log tags come from one header-only helper,ModuleIO::make_spin_tag(module_output/spin_tag.h).make_data_descand the DM(k), DM(R), H(R), H(R)-term, Vxc(R), dH/dR, dS/dR, band and PW/LCAO wave-function writers all use it, so nspin=2 runs label both spin channels consistently.Writing cube file <name>log lines inget_pchg_lcao.cpp/get_wf_lcao.cppthat duplicated theWrite ... to file:line now emitted bywrite_vdata_palgrid.has_efermiflag instead of string-matching the label, and the running-log banner for Fermi energy is emitted through a single path. The CSR header example indocs/advanced/elec_properties/hs_matrix.mdis updated for the, E_Fermi = <value> eVannotation.Tests:
test_hsr_writer.cppgains a text-CSR case covering per-spin Fermi sourced fromEfermi; fixed under single-rank__MPI(theParallel_Orbitalssource must callset_atomic_tracebeforegatherParallels, otherwiseget_nnrow_atomthrows onnat==0).hrs*.csr.refforscf_out_hsr_spin2to carry theE_Fermiheader.hsr_writerandoutput_logunittests.write_bands_test.cppaddsRunningLogNamesSpinChannels, which checks the nspin=2Write eigenvalues (spin up / spin down)log lines.test_spin_tag.cpp(MODULE_IO_spin_tag) coversmake_spin_tag.2. Cube filename normalization
.cubefilenames are now lowercase, short, and consistent with thechg.cube/chgs1.cube/pot.cube/wfi1s1k1re.cubefamily. Indices start from 1 and carry a short suffix indicating their meaning (s= spin,k= k-point,g= geometry step,st= state,i= band index).SPIN<n>_CHG.cube→chg.cube(nspin=1) /chgs<n>.cube(nspin=2, 4) in esolver_dm2rho, the same naming asctrl_output_fp. For nspin=4 all four density channels are now written, because theinit_chg=filereader readschgs1..4.cubeoncechgs1.cubeexists and aborts if one is missing.LDOS_<en>eV.cube→ldos_<en>ev.cube(cal_ldos; updatedstm.py,catch_properties.sh,result.ref,README;git mvLDOS.cube.ref→ldos.cube.ref).Exciton_avg_<type>_state<n>_spin<s>.cube→exc_<type>_st<n+1>_s<s+1>.cube(exciton_plotter; indices now start from 1). The matching.datslice filename is renamed the same way, and the conditional-slice.datbecomesexc_cond_<type>_slice_st<n+1>.datso one run no longer mixesExciton_cond_..._state<N>(0-based) withexc_..._st<N+1>names for the same state.chg_init.cppreads tau fromtau.cube/taus<n>.cube, aligning with the charge read path; the probe distinguishesnspin==1vsnspin>1. If neither exists, it falls back to the legacySPIN<n>_TAU.cubenames (3.10-LTS and earlier) with a warning, and it warns before the Thomas-Fermi fallback when no tau cube is found.std::remove("./support/OLD2_SPIN1_CHG.cube")intest_chg_extra.cpp(no code path generates that file).SPIN1_CHG.cubeetc.) are intentionally left in place.3. Parameterize ofs_running through IO output chains (Rule 5)
The IO output layer used to read
GlobalV::ofs_runningdirectly in many leaf and mid-level functions. This PR threadsstd::ofstream& ofs_runningexplicitly through:write_vdata_palgrid(cube_io.h),write_bands,save_dH_sparse/output_dHR/output_dSR/output_TR/output_SR/output_mat_sparse,cal_ldos_lcao/cal_ldos_pw/stm_mode_pw/ldos_mode_pw,write_elf,write_chg_init/write_pot_init,Get_pchg_pw/Get_wf_pwbegin + write_cube helpers,Get_pchg_lcao/Get_wf_lcaobegin,ctrl_iter_pw/ctrl_scf_pw/ctrl_runner_pw/ctrl_runner_lcao/ctrl_scf_lcao/ctrl_output_fp,write_elecstat_pot, plus their explicit template instantiations (CPU/GPU, double/complex).GlobalV::ofs_runningonce at the boundary; the IO layer itself no longer reads the global.Additionally, two historical default arguments are removed (Rule 5) and every caller now passes the value explicitly:
cube_io.h: removeddata_desc = "data"; all 22 call sites pass a descriptive string (see theme 1).write_bands.h: removed thenspin0 = 1default onnscf_bands. TheWrite eigenvalueslog line moved intowrite_bands, which ownsnspin0, sonscf_bandskeeps its upstream signature with nonspin0parameter.Governance notes
GlobalV::ofs_running; reads are now concentrated at the esolver/control boundary (ctrl_scf_lcaotakesofs_runningas a parameter andesolver_ks_lcaopassesGlobalV::ofs_runningat the boundary).write_cube'sndata_line = 6remains as pre-existing debt (out of scope here).<fstream>additions are in headers that now carrystd::ofstream¶meters, and each is required by the new signature.cube_io.hincludes the new header-onlyspin_tag.h, which depends only on<string>.Test plan
make -j 30passes.ctest --test-dir build -V -R "rho_io|write_bands|hsr_writer|dhs_sparse|output_mat_sparse|CHARGE_extra"— 8/8 pass (MODULE_CHARGE_extra,MODULE_IO_rho_io,MODULE_IO_write_bands,MODULE_IO_write_bands_parallel,MODULE_IO_hsr_writer_test,MODULE_IO_hsr_writer_test_parallel,MODULE_IO_dhs_sparse_writer_test,MODULE_IO_output_mat_sparse_test).python3 tools/03_code_analysis/code_quality_score.pyon the 66 changed C++ files at head2427fe3afe: average 73.5 (pass line 60), 52/66 pass. 14 files below 60 are pre-existing debt (tab_indentation, global_dependency, too_many_parameters) not introduced by this PR:ctrl_scf_lcao.cpp0,esolver_ks_lcao.cpp31,ctrl_output_pw.cpp32,cal_ldos.cpp33,dhs_sparse_writer.cpp41,ctrl_output_fp.cpp43,ctrl_runner_lcao.cpp50,esolver_ks_pw.cpp55,cal_ldos.h55,exciton_plotter.cpp56,test_chg_extra.cpp57,esolver_fp.cpp58,esolver_gets.cpp58,pos_op_writer.cpp59.CASES_CPU.txt/CASES_GPU.txt(03_NAO_multik): newly enablescf_out_hsr_spin2— it was previously in neither CI list, and itshrs{1,2}_nao.csr.refwere refreshed in this PR for theE_Fermiheader, so CI now covers the new format.props_mat.sh(compare_hr_csr): H(R) CSR bodies compare at 1e-8 with the, E_Fermi = ... eVannotation stripped, because it is printed withsetprecision(6). The E_Fermi values are then compared separately at 1e-3 eV, so the refreshed.refannotations are actually checked. A missing output or reference file now fails explicitly instead of passing through an empty process-substitution stream. S(R) carries no E_Fermi and is compared directly.tests/integrate) — to be run on the CI side;catch_properties.shupdated for the newldos_<en>ev.cubefilename.workflow_dispatch-only workflows (e.g.interface.yml).Review follow-up commits
Commits
1287175cb..3f5ac3b2awere pushed by @Critsium-xy during review. They address the review findings described above: the H(R) check silently passing, dm2rho naming (nspin=1 and nspin=4), legacy tau cube fallback, threadingofs_runningthrough ctrl_iter_pw, ctrl_runner_pw, ctrl_scf_lcao, write_dipole and write_elecstat_pot, the shared spin-tag helper, and spin tags in the PW/dH logs. These commits were checked withagent_governance_check.py(no errors, net GlobalV/PARAM references -6 across these commits) and a local shell test ofcompare_hr_csrcovering 5 cases (identical, E_Fermi off by 3e-4 eV, E_Fermi off by 0.1 eV, missing annotation, missing file). They were not built locally; build and test verification relies on the PR CI. Themake -j 30/ctestresults listed above were run by the author on an earlier head.