Repository navigation
Conversation
e5a8e55 to
cd8346b
Compare
|
Thanks a lot for your hard work on this PR — the overall direction looks great! Below are a few suggestions, ordered from the easiest to the more involved ones. Most should be quick to address, and I'm happy to discuss any of them.
|
|
@mohanchen For the last point (7.), no new ESolver is added: esolver_lr_grad.cpp is just a separate .cpp file from esolver_lr_lcao_tddft.cpp, corresponding to esolver_lr_lcao_tddft.h. |
Port the LR-Grad series (44 commits, 7c6398d..6aea1fc, all by maki49) onto current develop as a single squashed change, adapted to develop's large-scale refactor. Path/layout migration: module_lr -> source_lcao/module_lr module_ri -> source_lcao/module_ri (Exx_LRI.h -> exx_lri.h) esolver_lrtd_lcao -> source_esolver/esolver_lr_lcao_tddft ESolver_LR now lives in namespace ModuleESolver, not LR. Interface adaptation: - Grid integration: the Gint_Gamma/Gint_k objects and TGint<T> selector are gone. All call sites now use the free functions in ModuleGint (cal_gint_rho / cal_gint_vl / cal_gint_fvl); the gint pointer has been removed from LR_Force, LR_Density, OperatorLRHxc, HamiltLR/HamiltULR, the Z-equation Hamiltonians and the multiplier helpers. - PulayForceStress::cal_pulay_fs (grid overload) keeps the explicit `nspin` argument the gradient code needs; develop's own callers pass PARAM.inp.nspin. - Parallel_Orbitals::get_row_size(iat)/get_col_size(iat) -> get_nrow_atom(iat)/get_ncol_atom(iat). - elecstate::Potential::init_pot(istep, chg) -> init_pot(chg); get_effective_v -> get_eff_v; Structure_Factor::setup_structure_factor -> setup; Charge::allocate now takes kin_den. - ModuleIO::output_single_R takes a SparseWriteOptions struct. - ModuleIO::read_wfc_nao uses develop's signature (ik2iktot/nkstot/nspin). - ModuleBase::timer::tick -> start/end pairs. - LR_Util::gather_2d_to_full keeps develop's explicit-dimension form; scatter_full_to_2d is added for the Z-equation solver. - get_DMR_real_imag_part / set_HR_real_imag_part are now the templated, nat-free versions; module_bse call sites updated, the now-dead lr_util_hcontainer.cpp is removed. - DensityMatrix::cal_DMR is const (_DMR is mutable). Known gap: LR_Force::cal_force_exx_gs_dm_relaxed_diff still uses RI::LR from LibRI, which no longer derives from RI::Exx in the current LibRI master (commit 5c6c262 refactored it into a k-space CVCX/BSE helper). That single function does not compile; everything else does.
…X force kernel LibRI commit 5c6c262 repurposed RI::LR: it no longer derives from RI::Exx and lost set_Ds/cal_Hs/cal_force/force. Reintroduce just the piece the LR-TDDFT gradient needs as LR::ExxForceTwoDM, a thin RI::Exx subclass whose cal_force overload overwrites post_2D.saves["Ds_<suffix>"] with the left density matrix before delegating to Exx::cal_force. This reproduces the old RI::LR semantics (D_IJ from Ds_left, D_KL from set_Ds) without pinning LibRI. Also drop dm_trans/dmr_complex.cpp from the build: develop moved the DensityMatrix<complex,complex>::cal_DMR specialization into density_matrix.cpp, so keeping both gave a duplicate definition at link time.
…ose HSolverLR timer
Two crashes found running the H2/TDHF/SZ gradient case:
1. PulayForceStress::cal_pulay_fs (the LR overload in pulay_force_hcontainer.h)
passed the density matrix's spin count as `nspin` to ModuleGint::cal_gint_fvl
while supplying a single potential channel. Gint_fvl::cal_fvl_svl_ indexes
vr_eff_[is] for is<nspin, so with a two-channel DM (the test_force path feeds
in the ground-state DM) it read past the vector and segfaulted inside the
OpenMP region. The LR kernel produces one potential channel, so the grid
integrals now run with nspin=1 on the leading DM channel -- exactly what the
old `Gint_inout(is=0, ...)` calls did.
2. HSolverLR::solve started a timer that was never ended, so the second
(triplet) call threw "timer::start HSolverLR::solve". Added the matching
ModuleBase::timer::end on the common exit path.
Verified on H2 / TDHF / SZ (nspin=2, gamma-only):
- SCF reproduces the pre-port reference bit-for-bit (E_KohnSham -2.3110142036 Ry,
TOTAL-FORCE -14.7806682608 eV/Ang).
- test_force cross-check: GS Hxc force via potential -28.4711400896 vs via the
LR kernel -28.4711414920 eV/Ang (7 significant digits).
- The reproduced EXX force (31.3817781891) is exactly 2x the SCF EXX force
(15.6908890935), confirming ExxForceTwoDM replaces RI::LR correctly.
- Excitations 22.484 eV (singlet) / 13.813 eV (triplet); excited-state
gradients -0.6313 / -2.01497 eV/Ang, antisymmetric with ~1e-14 transverse
components.
solve_Z_lapack built a replicated Hessian via HamiltLR::matrix() but handed LAPACK the pX-distributed right-hand side `R` directly. lapack_linear_solver then copies n_global*nstates entries out of a buffer that only holds ld*nstates = nk*pX[0].get_local_size()*nstates -- an out-of-bounds read as soon as the local size is smaller than the global one, and a segfault on any rank whose local size is 0. Gather R into a global buffer first, mirroring the scatter of Z_full that already follows the solve. Serial results are bit-for-bit unchanged (verified on H2/TDHF/SZ: excited-state gradients -0.631306 / -2.01497 eV/Ang, all per-term forces identical to the pre-fix run).
`PotHxcLR::cal_v_eff` dominates an LR run: 74% of the total for 07_LiF/pbe (949 s of 1289 s over 541 calls). Roughly half of that was FFT; the rest was serial element-wise work and allocator traffic. Five changes, no algorithmic change: 1. OpenMP on the element-wise loops. Everything these loops call (`grad_rho`, `grad_dot`, the Hartree kernel) was already parallel; the integrands here were the only part still running on one thread. 2. Raw pointers instead of `.at()` (43 sites). The bounds check is a branch per access, ~10 per grid point, and it blocks vectorization. The outer-vector lookups (`drho_gs.at(0)`) are hoisted out of the loops, where they keep their bounds check for free. 3. A scratch pool instead of per-call temporaries. `drho`, `gdot_terms` and `vxc_tmp` were allocated and value-initialized on every call and then overwritten before being read -- ~450 MB of pointless memset per call at a 200^3 grid, plus the page faults. `grad_dot` assigns rather than accumulates, so even `vxc_tmp`'s zero-fill was dead. The pool is shared by all instances (a run holds three `PotHxcLR`) so it does not multiply. 4. `add_v_hartree` replaces `H_Hartree_pw::v_hartree`, which (a) re-did the forward FFT of rho^X that the GGA branch needs anyway -- 1 of the 10 FFTs per call was pure duplication, (b) reduced a Hartree "energy" of the transition density through `Parallel_Reduce::reduce_pool` every call, a collective nobody reads that also clobbers the global `H_Hartree_pw::hartree_energy`, and (c) returned a `matrix` by value, to which `v_eff += 2 * (...)` added a second full-size temporary. 5. The nspin=2 singlet/triplet combinations `v2rho2_uu -+ v2rho2_ud` and `2*vsigma_uu -+ vsigma_ud` are pre-contracted once per potential instead of being rebuilt at every grid point of every call, which also replaces two strided reads with one contiguous one. Costs 8-16 B/point, and only for closed-shell nspin=2. `PotGradXCLR::cal_v_eff` (the g^xc branch feeding the Z-vector RHS) gets 1-3 of the same treatment, for -20% to -28%. The shared pool matters more there: a `PotGradXCLR` is constructed inside the loop over excited states, so per-object buffers would never be reused at all. Measured (single node, 16 threads), analytic gradients unchanged: 01_Si/lda 93 s -> 51 s (-45%) cal_v_eff 66 -> 45 ms/call 01_Si/pbe 229 s -> 125 s (-46%) cal_v_eff 356 -> 268 ms/call 02_Li2/lda 165 s -> 86 s (-48%) cal_v_eff 559 -> 278 ms/call 02_Li2/pbe 530 s -> 253 s (-52%) cal_v_eff 2651 -> 1553 ms/call (The total also benefits from solving the Z-vector equation once instead of three times, a separate fix; the per-call figures above isolate this commit.) Excitation energies are identical to every printed digit; the nspin=1 cases agree to 1e-14, and of 60 force components per nspin=2 case, 2-3 differ by exactly one unit in the last printed digit (the Hartree prefactor is now associated differently). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMV7xFZ69hhc3HnVBHbD96
ABACUS-PR-BOT
left a comment
There was a problem hiding this comment.
Reviewed base 65a324ba3e5156c31f2818ccd93cd564985305ad -> head ab811c9b506165c7b1f1f3410c9306568672c461. The inline findings distinguish reproduced failures, small mathematical probes and conditional risks.
Follow-ups belong to the existing HSE, spin-basis tracking and operator-construction threads.
Remaining governance items from the earlier review:
- Existing interfaces still gain default arguments:
ao_to_mo.h:22,32,47andoperator_lr_hxc.h:42-44. Use explicit arguments or a clear overload. - New comma declarations remain, e.g.
lr_grad_os.cpp:46andforce_funcs.h:29; declare one variable per line. zeq_solver.h:9propagates a new implementation.hpp; move implementation out or record the narrow exception. The global-reference budget decreases by 32, so it is not a net-increase blocker.git diff --checkreports 33 whitespace errors; clean only the added/changed lines.- Non-blocking: correct the PR body's
lr_relax_degen_mode/lr_nvirttolr_degen_mode/nvirt.
Verification: MPI/LibXC/ELPA build without LibRI succeeded; 15 selected CTests, five additional four-rank tests, eight Python tests and 44 production-TU C++11 syntax checks passed. Independent finite differences and failure probes exposed the issues above. Governance: 0 mechanical errors, 216 warnings; 103/120 changed C++ files score >=60. Generated INPUT docs and CLI checks passed.
Not verified: EXX/HF/HSE/LRC force runs and BSE runtime (LibRI configuration lacked cereal), production open-shell finite differences, GPU/serial full builds, supercell replication and cutoff-boundary derivatives. CI status was unavailable.
Add a protected HamiltLR storage-only constructor and delegate the regular Casida constructor to it. Closed-shell Z operators initialize their own chains directly, keeping the shared transition density instead of replacing it after deleting an unused Casida chain. RI benchmark DMR initialization and the generic Operator implementation remain unchanged. Verification (OMP_NUM_THREADS=1, GNU/MPI, LibXC 5.2.3-kxc): - cmake --build build -j4 --target abacus_std_para MODULE_LR_zeqlin_solv - ctest --test-dir build --output-on-failure -R '^MODULE_LR_zeqlin_solv$' - Four-rank before/after runs of lr_tddft_lda_gamma and lr_tddft_hf_gamma: all 36 signed force components per case identical. - Four-rank lr_tddft_multik spectra and the two Gamma cases: five transition data files per case byte-identical before and after. - git diff --check; staged agent governance check: no errors. The focused existing runtime cases exercise both Z chains and ordinary Casida construction. No user documentation or INPUT changes are needed: this only changes internal construction and preserves numerical behavior.
Move kernel creation from PotHxcLR to the build_xc_kernel free function. Potentials now bind shared, already-evaluated coefficients and obtain the functional type from kernel-owned metadata rather than the current global XC state. Pass nspin explicitly and reject missing or mismatched kernels. Initialize spin metadata on every KernelXC path, including file and HF/RPA kernels, while preserving same-functional coefficient sharing. Migrate LR and BSE initialization to the explicit preparation interface. Kernel evaluation still uses the existing global XC setup internally; this change removes the ordering dependency from subsequent potential binding. Verification (GNU/MPI, LibXC 5.2.3-kxc, OMP/BLAS threads=1): - cmake --build build -j3 --target abacus_std_para MODULE_LR_pot_hxc_lrtd (targets built separately): passed. - ctest --test-dir build --output-on-failure -R '^MODULE_LR_pot_hxc_lrtd$': passed; four tests cover interleaved LDA/PBE binding, pointer sharing, file/HF/RPA spin metadata, and invalid kernel bindings. - The same four tests also passed using isolated staged sources, without the unrelated, uncommitted HSE component experiment or an ELPA link. - Four-MPI before/after LDA (nspin=1 and 2), PBE, HF, multi-k spectra, and LDA@PBE runs: all 162 signed force components identical and all 28 transition data files byte-identical. - git diff --cached --check and staged agent governance: no errors. The memory header is required by the shared_ptr factory declaration. No user documentation or INPUT update is needed for this internal refactor.
Remove lr_grad_solver=elpa, its implementation, dispatch, and test linkage. Keep ScaLAPACK Cholesky for positive-definite systems; report factorization failure on all ranks and recommend the LU-based scalapack option instead. Stop function timers before throwing so repeated calls report the actual matrix error rather than a timer-state error. KS ELPA solvers are unchanged. Parameter metadata documents the supported choices and failure behavior; user-facing YAML/Markdown are generated from this metadata. Verification (OMP/MKL/OpenBLAS threads=1): - GNU/MPI build: abacus_std_para, MODULE_LR_zeqlin_solv, MODULE_IO_input_test_para passed. - ctest --test-dir build --output-on-failure -R '^MODULE_LR_zeqlin_(solv|failure_mpi)$': 2/2 passed, including the four-rank indefinite/singular regression with a 60-second timeout. - All 14 solver tests passed on four ranks, including real/complex LU solutions for nonsingular indefinite matrices rejected by Cholesky. - Without ELPA: solver target rebuilt and the same two CTests passed. - InputParaTest.RemovedElpaZSolverIsRejected passed; --check-input rejects elpa with exit 1 and an explicit removal diagnostic. - CLI help, generated parameter YAML/Markdown, staged governance and git diff --cached --check verified; no governance errors.
Retain available-Z-space padding while diagnosing incomplete occupied, virtual, or KS band windows once during initialization. State explicitly that padding does not restore every orbital-response term and the force may differ from finite differences of the truncated excitation energy. Recommend full spin-resolved windows and force/window convergence checks. Remove the previous message suggesting nvirt could remain unchanged for a converged gradient. Update the generated INPUT reference metadata for nocc/nvirt; generated YAML/Markdown are not committed. Verification: - GNU/MPI executable built successfully with this change. - cmake --build build -j3 --target MODULE_LR_lr_util_pp and OMP_NUM_THREADS=1 ctest --test-dir build --output-on-failure -R '^MODULE_LR_lr_util_pp$': passed again before this commit. - Earlier four-rank, two-step BeH2 checks of this implementation passed for full, virtual-truncated, occupied-truncated, and band-truncated windows; full windows emit no warning, each truncated case warns once. - CLI help and generated parameter YAML/Markdown contain the caveats. - Staged governance: no errors; git diff --cached --check passed. This documents an approximation, not an exact truncated-window gradient fix. Archived FD measurements were not rerun for this documentation change.
Explain under lr_degen_thr that a threshold-defined group uses the mean excitation energy with a selected JT force. At exact degeneracy this gives the first-order branch slope without a mixing-coefficient response; with finite splitting inside the tolerance, energy-based line searches need not be consistent. A small spread does not bound the force error. Clarify that single-root energy and force resume only when threshold-based splitting leaves the tracked group with one member. The algorithm is unchanged and finite-interval energy-force consistency is not claimed. Verification: abacus_std_para rebuilt successfully; -h lr_degen_thr, --generate-parameters-yaml and docs/generate_input_main.py output all contain the limitation. git diff --cached --check and staged governance passed. User docs are generated from the updated parameter metadata.
Keep the historical absolute-force totals and add a separate signed comparison keyed by spin channel, state, atom, and Cartesian component. Require exact entry sets, reject malformed/duplicate/nonfinite data, and use each case's existing force_threshold (1e-6 eV/Angstrom). Only the separate LR force tables are read; GS and excited-state total forces and the existing state-tracking validators are unaffected. Add component references for the five existing LDA/PBE/HF force cases from pre-change logs. Their absolute sums match the existing scalar references; no existing numerical values or tolerances are changed. These are behavior baselines, not independent physical validation. No finite-difference calculations are added to CI. Verification (OMP/MKL/OpenBLAS threads=1): - python3 -m unittest discover -s tests/integrate/validation_tools -p test_lr_force_check.py -v: 7 tests passed, including sign reversal, atom/axis/state permutations, missing data, duplicates/nonfinite values, tolerance handling and shell collector failure propagation. - ctest --test-dir build --output-on-failure -R '^lr_force_collector$': passed; the lightweight unit tests are registered with CTest. - Real Autotest.sh runs of all five cases, sequentially with 1 and 4 MPI processes: all 153 signed components and existing properties passed, without warnings. No FD calculations were performed. - bash -n props_lr.sh, staged governance and git diff --cached --check: passed with no findings.
The serial read-item tests still expected the old lr_grad_solver option list and build-dependent ELPA diagnostic. Update these assertions for the removed option and require ELPA rejection independently of build flags. Keep valid solver and serial ScaLAPACK rejection checks unchanged. Verification (OMP/MKL/OpenBLAS threads=1): - Reproduced InputTest.Item_test2 locally with the same two assertion failures reported by MODULE_IO_read_item_serial in CI. - cmake --build build -j3 --target MODULE_IO_read_item_serial: passed. - ctest --test-dir build --output-on-failure -R '^MODULE_IO_read_item_serial$': passed; all seven tests passed. - Staged governance and git diff --cached --check passed. Only test expectations change; no production behavior changes.
|
I will mention again here: the esolver directory currently contains too many implementation details specific to LR algorithms. For instance, cal_nupdown_form_occ—along with several other functions—should not reside here and needs to be relocated or refactored. Please streamline the esolver_lr code to significantly reduce its line count and clutter." Furthermore, while there are still some legacy LCAO-related codes in esolver that shouldn't be there, that is not a justification to follow suit, especially since I don't have the time to clean up the LCAO parts myself. Leaving so many LR details here only increases my workload, and realistically, you are the one best positioned to help keep this clean." |
|
To follow our design specifications: esolver should serve as the cockpit control panel of a car—it needs to remain as clean and simple as possible. While a car naturally relies on countless intricate parts and wiring to function, these internals should be encapsulated under the hood, not exposed on the dashboard. The purpose of esolver is to present a high-level physical workflow, not the granular mechanics of how every individual physical quantity is computed. It is sufficient for the reader to know that a quantity has been computed. Furthermore, we enforce strict naming conventions for functions inside esolver—you cannot simply introduce arbitrary helper functions. Just like modern vehicle control panels maintain standard layouts so drivers can transition seamlessly between different cars, esolver maintains a standardized interface across solvers. Unsanctioned additions degrade the readability and consistent experience that esolver is designed to provide. |
Resolve the solver-description conflict while retaining the LR force limits. Move the reviewed LR implementation headers to compiled source files with explicit real/complex instantiations, and move occupation counting, parameter validation, two-center setup and kernel/potential assembly out of ESolver. Preserve kernel sharing and the existing preparation order. Update CMake, GNU Makefile objects, tests and the RPA spectrum caller. No LR INPUT behavior, force formula or integration reference is changed by this refactor. Existing global accesses are moved without increasing their count; the new initialization helpers receive their dependencies explicitly. The historical, large RI exx_lri.hpp implementation is outside this cleanup. Verification: - GNU/MPI abacus_std_para build and --version: v3.11.0-beta10. - No-MPI/no-ELPA lr and esolver targets build successfully. - Six focused LR CTests and MODULE_IO_read_item_serial pass. - All 14 Z linear-solver tests pass with four MPI ranks. - Six integration cases pass at one and four ranks (OMP_NUM_THREADS=1): LDA spin 1/2, PBE, HF, unrestricted HF forces, and multi-k spectra. All 153 signed force components pass on each layout; references unchanged. - Staged governance checker: no errors. Header warnings reflect declarations and owned standard-library types; existing global dependencies are retained.
|
@mohanchen Refactored in 398a482. All the newly added .hpp files are changed into .h+.cpp. exx_lri.hpp is an existing file and I only change a few lines, so refactor it is beyond the feature's scope. Some member functions are extracted outside from ESolverLR. Ready for review again. |
| #endif | ||
| /// cached aliases, read once here instead of at every log/flag check below | ||
| std::ofstream& ofs_running_ = GlobalV::ofs_running; | ||
| const int nspin_ = PARAM.inp.nspin; |
There was a problem hiding this comment.
Not good to use PARAM here— new code should avoid this. The reasoning comes from the project governance rules and testing practice:
- Violates the cross-layer control rule
Governance rule 1 explicitly forbids increasing cross-layer control throughGlobalV ,GlobalC , orPARAM . ReadingPARAM.inp.xxx in a member initializer list implicitly injects the global input configuration into the class — nothing in the constructor signature reveals which INPUT parameters the class depends on.
- Poor testability
To drive such a class in a unit test, you must set up global PARAM first, or resort to a#define private public hack — and the governance checker now blocks any net increase of such hacks. The fix pattern named in the governance doc is exactly this: pass INPUT values as explicit arguments (seeRelax_Criteria andK_Vectors::read_kpoints ).
- Hidden initialization-order risk
Member initializer lists execute before the constructor body, so the code silently requires "PARAM must be filled before construction" — with no mechanism to enforce or even see that precondition.
Recommended pattern : read PARAM once at the wiring point (e.g., Driver / ESolver construction), assign to named local variables, then pass them explicitly to the constructor; the initializer list only does direct parameter passthrough (this also satisfies governance rule 13: no non-trivial function calls in the initializer list).
| #ifdef __MPI | ||
| const int i1 = 1; | ||
| const int ivirt = nocc + 1; | ||
| pdgemm_(&transa, &transb, &naos, &nocc, &nvirt, |
There was a problem hiding this comment.
Do not callpgemm ordgemm directly; you must use the math library connectors provided insource_base .
| X, &i1, &i1, px.desc, | ||
| &beta, X_ao_occ, &i1, &i1, px_ao_occ.desc); | ||
| #else | ||
| dgemm_(&transa, &transb, &naos, &nocc, &nvirt, |
There was a problem hiding this comment.
Do not callpgemm ordgemm directly; you must use the math library connectors provided insource_base .
| #ifdef __MPI | ||
| const int i1 = 1; | ||
| const int ivirt = nocc + 1; | ||
| pzgemm_(&transa, &transb, &naos, &nocc, &nvirt, |
There was a problem hiding this comment.
Do not callpgemm ordgemm directly; you must use the math library connectors provided insource_base .
| X, &i1, &i1, px.desc, | ||
| &beta, X_ao_occ, &i1, &i1, px_ao_occ.desc); | ||
| #else | ||
| zgemm_(&transa, &transb, &naos, &nocc, &nvirt, |
There was a problem hiding this comment.
Do not callpgemm ordgemm directly; you must use the math library connectors provided insource_base .
| if (info) { std::cout << "ERROR: Lapack solver dgetrf, info=" << info << std::endl; } | ||
| // 3. Solve Ax=b | ||
| const char trans = 'N'; | ||
| dgetrs_(&trans, &n, &nrhs, A_copy.data(), &n, ipiv.data(), x, &n, &info); |
| // 2. LU decomposition: A->LU | ||
| std::vector<int> ipiv(n); // pivot indices | ||
| int info = 0; | ||
| dgetrf_(&n, &n, A_copy.data(), &n, ipiv.data(), &info); |
| @@ -143,8 +316,8 @@ namespace LR_Util | |||
| delete[] rwork; | |||
There was a problem hiding this comment.
use std::vector instead of new/delete?
| @@ -143,8 +316,8 @@ namespace LR_Util | |||
| delete[] rwork; | |||
| delete[] work2; | |||
There was a problem hiding this comment.
use std::vector instead of new/delete?
| int spin_population_difference(const ModuleBase::matrix& wg, const int kpar, const int rank_in_pool) | ||
| { // only for nspin=2 | ||
| const int& nk = wg.nr / 2; | ||
| auto occ_sum_k = [&](const int& is, const int& ib)->double { double o = 0.0; for (int ik = 0;ik < nk;++ik) { o += wg(is * nk + ik, ib); } return o;}; |
There was a problem hiding this comment.
. Harder to Debug (the most practical issue)
- Breakpoints are set per line: the entire line gets a single breakpoint — you cannot stop at the loop body or the
o += ...accumulation statement to inspect intermediate values - Step-through granularity is per line: multiple statements on one line cannot be stepped individually
- Crash/out-of-bounds stacks only report line numbers: this line contains index arithmetic
is * nk + ik, container accesswg(...), and loop control — an error message cannot immediately tell you which step failed
2. Version-Control Unfriendly
- Coarse diff granularity : changing any single character (e.g., renaming
nk) marks the whole line red in the diff, forcing reviewers to hunt character-by-character for the actual change - Polluted git blame : any later small edit overwrites blame ownership for the entire line, losing the "who wrote which part" history
- Higher merge-conflict probability : parallel edits to different parts of the same line always conflict and cannot be auto-merged by git
3. Readability and Inspectability
- Readers must parse statement boundaries themselves: the lambda signature, loop header, loop body, and return value — four layers of logic crammed together
- Expressions like
ik < nkandis * nk + iklack spacing and named intermediate variables, so operator precedence must be mentally verified — the same spirit as project rule 1. modify some variable name in LCAO_gen_fixedH::build_Nonlocal_beta() #14 (no conditional expressions inline in argument lists; assign to a named local first): every value should be inspectable - Static analysis / coverage tools (gcov, llvm-cov) count per line; multiple statements on one line distort coverage data
4. Higher Cost of Modification and Refactoring
- Adding a log statement, an assertion, or extracting the loop body requires manually re-wrapping and reformatting first — a "one-line addition" becomes a "rewrite of a whole block"
- Compact writing hides small errors (e.g., the missing space before
++ikhere makes the loop structure easy to miss when skimming)
5. An Independent Issue in This Line: [&] Capture-All
The capture list says nothing about what the lambda actually depends on (in realitywg andnk ); the reader must read the entire enclosing function to confirm it does not accidentally capturethis or other mutable state. Writing[&wg, nk] would be far clearer.
Move force flags, functional selection and logging into explicit constructor inputs at the ESolver wiring points. Replace the global kernel_nspin helper with an explicit, validated spin count for gradient potentials. Use BlasConnector::gemm_cm and ScalapackConnector::gemm for the EDM contractions, preserving column-major layouts, submatrix offsets and complex adjoints. Use the existing column-major LAPACK getrf/getrs wrappers for LU solves, replace eigenproblem work arrays with vectors, and expand occupation counting with explicit lambda captures and inspectable steps. No INPUT metadata or integration references change: these are internal interfaces and equivalent numerical operations. Tests additionally check explicit gradient-potential spin metadata and reject mismatches. Verification (OMP_NUM_THREADS=1, BLAS threads=1): - GNU/MPI abacus_std_para build passes. - No-MPI/no-ELPA lr and esolver builds pass; the serial EDM CTest passes. - Five focused MPI-build CTests pass. - Four-rank EDM contractions: 2 tests pass; Z solvers: 14 tests pass. - Six original LR cases pass at both one and four ranks, without comparison warnings; all 153 signed force components pass per layout. - Staged governance checker has no errors; git diff --cached --check passes.
Add real/complex heev overloads to the column-major LAPACK connector and replace the direct dsyev/zheev calls in LR utilities. Preserve the original QR drivers, real workspace query, workspace sizes and matrix layout; the row-major zheev connector would otherwise require an extra transpose. Verification: MODULE_LR_lr_util_pp, MODULE_LR_zeqlin_solv and MODULE_LR_zeqlin_failure_mpi CTests all pass (3/3), including real/complex eigenpair residual checks and the four-rank solver failure regression. OMP_NUM_THREADS=1 and BLAS threads=1. No INPUT or reference changes.
|
@mohanchen All fixed in 17d5a66:
Ready for review again. |
|
For issues that a reviewer has pointed out before, please learn to draw inferences from one instance — in other words, fix all similar problems throughout the code as well, because the reviewer may have missed some of them too. Reviewing more than ten thousand lines of code is already an enormous burden on the reviewer, so please don't count on anything slipping through by luck. The less thorough your fixes are, the more they erode the reviewer's trust. Merging poor-quality code into the community does not reduce the submitter's workload, nor is the reviewer the only one who suffers — it harms the interests of everyone in the community who contributes to the ABACUS codebase. Please keep this sense of responsibility in mind. |
|
For example, when I say that functions like |
| char transb = 'N'; | ||
| const double alpha = 1.0; | ||
| const double beta = 0; | ||
| pdgemm_(&transa, &transb, &naos, &nocc, &nvirt, |
There was a problem hiding this comment.
For issues that a reviewer has pointed out before, please learn to draw inferences from one instance — in other words, fix all similar problems throughout the code as well, because the reviewer may have missed some of them too. Reviewing more than ten thousand lines of code is already an enormous burden on the reviewer, so please don't count on anything slipping through by luck. The less thorough your fixes are, the more they erode the reviewer's trust. Merging poor-quality code into the community does not reduce the submitter's workload, nor is the reviewer the only one who suffers — it harms the interests of everyone in the community who contributes to the ABACUS codebase. Please keep this sense of responsibility in mind.
| char transb = 'N'; | ||
| const std::complex<double> alpha(1.0, 0.0); | ||
| const std::complex<double> beta(0.0, 0.0); | ||
| pzgemm_(&transa, &transb, &naos, &nocc, &nvirt, |
| char transb = 'T'; | ||
| const double alpha = 1.0; | ||
| const double beta = 0; | ||
| pdgemm_(&transa, &transb, &naos, &nvirt, &nocc, |
| char transb = 'T'; | ||
| const std::complex<double> alpha(1.0, 0.0); | ||
| const std::complex<double> beta(0.0, 0.0); | ||
| pzgemm_(&transa, &transb, &naos, &nvirt, &nocc, |
| char transa = 'N'; | ||
| char transb = 'T'; | ||
| const double beta = add ? 1.0 : 0.0; | ||
| pdgemm_(&transa, &transb, &nrow, &nrow, &ncol, |
| // Branch on THIS kernel's own GGA-ness, not the ground state's (see `cal_v_eff`). | ||
| if (!kxc.is_gga()) // LDA: only $g^{\rho\rho\rho}$ survives | ||
| { | ||
| const double* const r1u = rho1[0]; const double* const r1d = rho1[1]; |
There was a problem hiding this comment.
Please break one-liner code into multiple lines.
| #ifdef _OPENMP | ||
| #pragma omp parallel for schedule(static) | ||
| #endif | ||
| for (int ir = 0;ir < nrxx_;++ir) { vout[ir] += ModuleBase::e2 * (v_tmp[ir] + div[ir]); } |
There was a problem hiding this comment.
Please break one-liner code into multiple lines.
| // the four (sigma, sigma') blocks: which pre-contracted arrays to read, | ||
| // which vsigma component multiplies drho^X, and which v2rho2 component. | ||
| const int blk = ispin_op[0] << 1 | ispin_op[1]; | ||
| const Vec3* rs = nullptr; const Vec3* su = nullptr; const Vec3* sd = nullptr; |
There was a problem hiding this comment.
Please break one-liner code into multiple lines.
| if (rho_core) | ||
| { | ||
| const double fac = 1.0 / nspin; | ||
| for (int is = 0; is < nspin; ++is) { for (int ir = 0; ir < nrxx; ++ir) { rho[ir * nspin + is] += fac * rho_core[ir]; } } |
There was a problem hiding this comment.
Please break one-liner code into multiple lines.
| #ifdef _OPENMP | ||
| #pragma omp parallel for collapse(2) schedule(static, 1024) | ||
| #endif | ||
| for (int is = 0; is < nspin; ++is) { for (int ir = 0; ir < nrxx; ++ir) { rho[ir * nspin + is] = rho_gs[is][ir]; } } |
There was a problem hiding this comment.
Please break one-liner code into multiple lines.
Convert remaining AO/MO, density-difference, Gram-spectrum and distributed transpose calls. Add column-major general-eigenproblem wrappers and preserve existing algorithms, layout and conjugation. Permit const input matrices in the distributed transpose connector. Six focused CTests pass; four-rank CVCX and difference-density tests pass (four tests each). New real/complex non-Hermitian eigenvalue regressions pass. OMP and BLAS threads are one. Internal interfaces only; no INPUT or reference changes.
|
All fixed throughout the additions:
For Ready for review again. |
Features
Refactors
Other Behavior changes
1e-6to1e-14, and the gradient cutoff from1e-10to1e-20. This can change excitation energies even when forces are disabled. A four-rank A/B run reproduces the LDA gamma reference changes. The periodic Si multi-k case has a minimum spin density of approximately1.29e-3, above the old cutoff; its four excitation energies agree at 17-digit output precision between the two settings.nspin=1singlet response uses the factor-two Hartree/XC contribution, while ground-stateS1_gsretains weight one. Existing single-spin excitation results can therefore change. BSE grid initialization usesS1_gsbecauseHamiltBSEalready applies the singlet factor; the existing RI BSE path is unaffected by this grid correction.1e-6when evaluating XC derivatives. This can change HSE06 response energies and gradients near vanishing density gradients; it does not apply this clamp to ordinary PBE.kxc, rather than allowing the last functional to overwrite earlier contributions. This affects the XC contribution to analytic excited-state forces.lr_nvirtcontinues to select the response basis. The gradient can depend on the available ground-state band window even when the response basis is held fixed.test_forceruns.