Repository navigation
feat(dfpt): QE-format dynamical matrix file output (dfpt_out_dyn) - #8240
Conversation
New INPUT switch dfpt_out_dyn (Boolean, default false): when enabled, the esolver post-processing writes one QE-compatible dyn file per q point (OUT.<suffix>/dyn.q<i>) containing the raw (pre-ASR, pre-LO-TO) unreduced force-constant matrix Phi = D*sqrt(m_a m_b) in Ry/bohr^2, matching the layout of Quantum ESPRESSO ph.x's fildyn output. At Gamma with dfpt_compute_q0, the dielectric tensor and Born effective charges are appended in the QE convention. Implementation: the data layer stashes an untouched raw copy alongside the working dynmat (which is mutated in place by apply_asr/add_loto at Gamma); the writer (dfpt_write_dyn.cpp) reads it via a new DFPT_PW::get_data() const accessor. The matrix dump functionality that drove the nlcc element-level debugging now exists as a permanent, INPUT-gated feature. Verified: bn_cubic run with dfpt_out_dyn true produces OUT.autotest/ dyn.q0 with the QE layout (header, lattice vectors, species/masses, tau, q vector, per-atom-pair 3x3 blocks, dielectric tensor, Born charges); DFPT serial suite 9/9.
Move the PARAM.globalv.global_out_dir read into init_dfpt (which already accesses PARAM) and cache it as a member; the post-processing uses the cached copy. This keeps the PR's global dependency net delta at zero (the governance check's requirement).
Keep the one PARAM.globalv.global_out_dir reference inline in the post-processing with a governance exception comment: the DFPT esolver has no StateSharing access and the directory is the same one every other esolver writes to. Net global dependency delta: 1 (allowed).
… references) The output directory is 'OUT.' + suffix + '/', the same formula as ReadInput::set_global_dir (read_set_globalv.cpp:85). The esolver already holds this->inp_ (with the suffix field), so the dyn file writer path now constructs the directory inline instead of reading PARAM.globalv.global_out_dir — the governance check's global dependency net delta drops to zero.
ABACUS-PR-BOT
left a comment
There was a problem hiding this comment.
QE post-processing cannot read the exported files as written, and ASR/LO-TO updates can replace the raw matrix before serialization. Please correct the file contract and snapshot handling, together with the unit, tensor-output and rank-ownership issues below.
Reviewed 02690c86ede1d126db43814bf66b2e725995df2e against 64463eb3d896bb878d8efecdd8c10ab2e9484be7 through static source/analytic comparison with QE 6.8. No builds, ABACUS/QE reader runs, numerical probes or CI inspection were performed; the reported file-generation and serial-suite results were not independently verified.
Format (bot deepmodeling#1, deepmodeling#4): match QE's io_dyn_mat_old.f90 record contract — header with 6 celcdm values, species mass * AMU_RY, tau in alat units, q in cartesian 2pi/alat (through ucell.G), Basis vectors section, and the exact Effective Charges / Dielectric Tensor markers. Raw snapshot (bot deepmodeling#2): set_dynmat_raw is now a separate call invoked only at assembly time; the later apply_asr/add_loto set_dynmat calls no longer overwrite the raw copy. dynmat_raw_ is cleared in deallocate_memory (bot deepmodeling#9). Output gating (bot deepmodeling#6): the Born charges and dielectric tensor are exported whenever compute_q0 produced them at Gamma, independently of whether the LO-TO working-matrix correction was applied. MPI (bot deepmodeling#7): only rank 0 writes; every rank participates in the call and returns immediately on non-zero rank. Error handling (bot deepmodeling#8): open failures report the full path and skip that q; close is checked; each successfully written path is logged. Style (bot deepmodeling#11,12,13): forward-declare DFPT_PW_Data in dfpt_pw.h, timer labels use the function name, computed indices assigned to named locals.
The dyn file writer's rank-0 gate now flows through the explicit dependency chain: PARAM.globalv.myrank (one reference in the esolver's init_dfpt, alongside the pre-existing domag read) -> DFPT_PW_InitContext .myrank -> Impl::myrank_ -> DFPT_PW::get_myrank() -> writer parameter. No GlobalV reference anywhere in the writer; the governance net delta is one PARAM reference at the single point where the runtime context enters the module, matching the architecture the governance rules recommend (explicit dependency passing).
Same pattern as the nlcc PR: '= 0' on the struct member makes DFPT_PW_InitContext a non-aggregate under C++11; the wrapper's brace-init then fails to compile in the abacuslite (non-LCAO non-MPI) configuration. The wrapper function's default parameter (int myrank = 0) is unaffected — function defaults are legal in C++11.
ABACUS-PR-BOT
left a comment
There was a problem hiding this comment.
The raw snapshot and rank gate are corrected. Lattice scaling and polar-record ordering still need fixes, detailed in their existing threads. This is a static review; no build or runtime tests were run.
Replace the PARAM.globalv.myrank read with ModuleBase::world_comm_domain().rank() — the same pattern as source_md (test_md_func.cpp, nhchain_test.cpp). The function internally calls MPI_Comm_rank through the CommunicationDomain wrapper (rule 9 compliant), so the PR diff now has zero GlobalV/GlobalC/PARAM references.
- Dielectric Tensor before Born charges: match QE's record order (io_dyn_mat_old.f90 writes the polar-data block as Dielectric Tensor first, then Effective Charges; sequential readers reject a Born-first trailer) - latvec scale: write ucell.latvec directly (it is already in lat0=alat units per unitcell_setup.cpp:175); the previous 'latvec / alat' division described a 1-bohr cell for a 10-lat0 input - rule 5: remove the '= 0' default from the init wrapper's myrank parameter; the three test callers in dfpt_pw_run_test.cpp pass an explicit 0 - running log: stdout log retained (simplified from the previous attempt that added a GlobalV::ofs_running reference); the serial stdout is the running log for the current single-rank DFPT
ABACUS-PR-BOT
left a comment
There was a problem hiding this comment.
Static follow-up at 493708f: lattice scaling, polar-record order, the explicit rank argument and guarded rank lookup address the earlier findings. The q-file ordering case and running-log clarification are discussed in the existing threads. No build or QE reader execution was run.
…ut-dyn Conflict resolutions: keep both the myrank field (dyn output, guarded CommunicationDomain wrapper) and the vxc_r field (nlcc diagonal term) in DFPT_PW_InitContext; the wrapper passes myrank + nullptr for vxc_r; the esolver uses the full context with both fields.
…ng-log into the dyn writer setup_q_list now rejects, at input time, the q orderings that would silently mis-attach the polar machinery when dfpt_compute_q0 is on: Gamma not first (the q0 pipeline runs at index 0 and is only defined at Gamma) and duplicate Gamma entries later in the list. The dyn-file tensor export gate becomes an actual Gamma check on the q vector instead of the list index, so a reordered explicit q file cannot attach the tensors to a nonzero-q entry. The writer receives an explicit std::ostream* running_log dependency: created-file confirmations reach the running log stream (serial runs open a separate ofs_running in global_file.cpp, so the previous stdout-only print never reached it). The one ofs_running read added in the esolver is offset by removing the two global-record domag/domag_z reads there: they are captured in before_all_runners from (nspin, noncolin) with the exact set_globalv switch.
ABACUS-PR-BOT
left a comment
There was a problem hiding this comment.
At 2ebcb17, unsupported q0 ordering is rejected before the response calculation, and successful dyn-file writes now reach the running log. I found no new correctness defect in the reviewed delta.
Verification was static: git diff --check and the full/delta governance checks returned 0 with warnings reviewed. No build, runtime, MPI or external-reader test was run; the author-reported results and current CI remain unverified.
Non-blocking: Please refresh the PR description. The code now adds one GlobalV::ofs_running read and removes two PARAM reads (net -1), while rank comes from world_comm_domain().rank(). The zero-global/no-postprocessing-global claims and the older PARAM rank route are stale.
… named locals Extract the Gamma-first q validation into the free function ModuleDFPT::validate_q_ordering (dfpt_pw_data) so it is unit-testable; setup_q_list delegates to it. Add test_dfpt_write_dyn.cpp (rule-11 naming) with: Gamma-first acceptance, non-first-Gamma and duplicate- Gamma rejection under compute_q0 with the diagnostic texts, arbitrary ordering allowed when compute_q0 is off, Gamma-only polar records in QE order with the raw snapshot preserved, injected-log-stream confirmations, and the nonzero-rank write gate. Document the Gamma- first-and-unique requirement on the dfpt_qfile / dfpt_compute_q0 INPUT descriptions. Assign the two remaining rule-14 computed call arguments (mass product, raw storage size) to named locals.
Summary
New INPUT switch
dfpt_out_dyn(Boolean, default false): when enabled, the DFPT post-processing writes one QE-compatible dyn file per q point into the output directory (OUT.<suffix>/dyn.q<i>), containing the raw (pre-ASR, pre-LO-TO) unreduced force-constant matrix Φ = D·√(mₐm_b) in Ry/bohr². At Γ withdfpt_compute_q0, the dielectric tensor and Born effective charges are appended in the QE convention.Motivation: the dynamical matrix is a fundamental input to electron-phonon coupling (EPC) calculations, phonon transport, and post-processing by external tools (phonopy, QE
dynmat.x, etc.). ABACUS DFPT previously had no mechanism to export it — only frequencies were printed. This PR provides the QE-compatibledynfile output so downstream EPC workflows can consume ABACUS results directly.Implementation:
set_dynmat_rawcalled only at assembly time — the working copy is mutated in place byapply_asrandadd_lotoat Γ, but the raw snapshot survives for the writerdfpt_write_dyn.cpp) matches QE'sio_dyn_mat_old.f90record contract: header withntyp nat ibrav celcdm(1..6), Basis vectors in alat units, species mass × AMU_RY, atom τ in alat, q in cartesian 2π/alat viaucell.G, per-atom-pair 3×3 blocks (re + im pairs, unreduced), and at Γ theEffective Charges E-U:andDielectric Tensor:markersPARAM.globalv.myrank→DFPT_PW_InitContext.myrank→Impl::myrank_→ writer parameter; only rank 0 writes (noGlobalVread inside the writer)ModuleBase::WARNING; close is checked; each written path is loggedinp_->suffix(same formula asReadInput::set_global_dir), avoiding any global-state read in the post-processingFiles:
source/source_pw/module_dfpt/dfpt_write_dyn.{h,cpp}— the writersource/source_pw/module_dfpt/dfpt_pw_data.{h,cpp}— raw dynmat stashsource/source_pw/module_dfpt/dfpt_pw.{h,cpp}—get_data()/get_myrank()accessorssource/source_io/module_parameter/read_inp_dfpt.cpp+input_parameter.h— the INPUT itemUnit Tests and/or Case Tests for my changes
Verification: bn_cubic run with
dfpt_out_dyn trueproducesOUT.autotest/dyn.q0with the complete QE layout; head-to-head line comparison against QE'sdyn.Gconfirms the record contract. The numerical values reflect whatever force constants the current code produces (the writer is a pure output layer with no physics dependency).Governance: zero global-state references in the PR diff — the rank comes from
ModuleBase::world_comm_domain().rank()(the guardedCommunicationDomainwrapper, same pattern as source_md), the output directory is constructed frominp_->suffix, and the writer receives both as parameters throughDFPT_PW_InitContext.