source_relax: give BFGS_Basic and Ions_Move_BFGS an explicit test seam, removing four access hacks - #7984
Merged
mohanchen merged 1 commit intoSep 17, 2026
Conversation
…m, removing four access hacks bfgs_basic_test and ions_move_bfgs_test drove the BFGS machinery through `#define private public` / `#define protected public`. Both now go through named accessors on the two classes instead, so the access specifiers mean what they say. BFGS_Basic gains eleven reference accessors -- get_pos(), get_grad(), get_move(), get_pos_p(), get_grad_p(), get_move_p(), get_save_flag(), get_tr_min_hit(), get_wolfe_flag(), get_inv_hess(), get_bfgs_ndim() -- and seven wrappers for the protected/private steps the tests drive one stage at a time: allocate_basic_for_testing(), new_step_for_testing(), reset_hessian_for_testing(), save_bfgs_for_testing(), update_inverse_hessian_for_testing(), check_wolfe_conditions_for_testing() and compute_trust_radius_for_testing(). Ions_Move_BFGS gains get_init_done(), get_first_step(), bfgs_routine_for_testing() and restart_bfgs_for_testing(). The accessors return non-const references because the tests both seed and inspect this state -- inv_hess alone is 34 reads and 20 writes across the two files, and every member except pos_p is written somewhere. `T& get_x()` matches the convention already used in ~83 places in the tree (get_allocator(), get_nonlocal(), get_nproj(), ...). The method wrappers follow the one existing precedent for a test-only entry point, set_density_rotations_for_testing() in symm_rotation.h, and carry a comment saying production code must keep calling the protected/private names directly. Worth stating plainly for review: this adds 21 public members that only the tests use, and for the seven method wrappers a public *_for_testing() forwarder is a weaker boundary than the alternative -- moving BFGS_Basic's six private declarations into its already-large protected section and letting the fixtures derive from the class, which would have changed no signature and added no public API. That alternative was considered and not taken. No production logic changed. No assertion or expected value changed; the tests call the same code with the same inputs through the new names. ions_move_methods_test keeps its two macros: it reaches through Ions_Move_Methods' aggregated members (imm.bfgs.tr_min_hit, imm.bfgs.first_step, imm.bfgs_trad.is_initialized), which needs accessors on Ions_Move_Methods as well and is a separate change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mohanchen
approved these changes
Sep 17, 2026
Zanthoxylum
pushed a commit
to Zanthoxylum/abacus-develop
that referenced
this pull request
Sep 17, 2026
This was referenced Sep 18, 2026
Merged
mohanchen
pushed a commit
that referenced
this pull request
Sep 18, 2026
… hacks (#7989) Five macros across three modules, each the last one in its module. The three files are independent -- no shared production class -- and all follow the accessor pattern established in #7984. ions_move_methods_test (2 macros) Ions_Move_Methods already had public get_converged() and get_update_iter(), so every read was already covered; only the writes and the two aggregated sub-optimisers needed anything. It gains set_converged(), set_update_iter(), get_etot_info(), get_bfgs() and get_bfgs_trad(), and Ions_Move_BFGS2 gains a const get_is_initialized(). The cross-object reads -- imm.bfgs.tr_min_hit, imm.bfgs.pos, imm.bfgs.inv_hess and the rest -- now chain through the Ions_Move_BFGS / BFGS_Basic accessors added in #7984, which is why that PR had to land first. Note a derived-fixture approach could not have worked here at all: C++ only lets a derived class reach a base's protected members through objects of its own type, and Ions_Move_Methods does not derive from BFGS_Basic. esolver_dp_test (2 macros) runner() needs a real DP model file, so the test seeds the computed results and checks that cal_energy() / cal_force() / cal_stress() hand them back. Those are writes as well as reads, so ESolver_DP gains four reference accessors: get_atype(), get_dp_potential(), get_dp_force(), get_dp_virial(). lj_pot_test (1 macro) before_all_runners() derives the LJ tables in three steps and the test drives each on its own. ESolver_LJ gains six const accessors -- get_search_radius(), get_lj_rcut(), get_lj_c6(), get_lj_c12(), get_en_shift(), get_lj_virial() -- and three wrappers: rcut_search_radius_for_testing(), set_c6_c12_for_testing() and cal_en_shift_for_testing(). All six reads are read-only here, hence const. No production logic changed. No assertion or expected value changed. Tree-wide count goes 28 -> 23, and source_relax, source_esolver and source_md join source_base, source_basis, source_hamilt, source_hsolver, source_lcao, source_main and source_psi at zero. What remains is source_io (11, being restructured, so left alone), source_estate (10) and one file each in source_cell and source_pw -- and #7987 / #7988 already take source_cell's and three of source_estate's. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
bfgs_basic_testandions_move_bfgs_testdrove the BFGS machinery through#define private public/#define protected public. Both now go through namedaccessors on the two classes, so the access specifiers mean what they say. Four
macros removed;
source_relaxdrops from 6 to 2.BFGS_BasicEleven reference accessors —
get_pos(),get_grad(),get_move(),get_pos_p(),get_grad_p(),get_move_p(),get_save_flag(),get_tr_min_hit(),get_wolfe_flag(),get_inv_hess(),get_bfgs_ndim()— andseven wrappers for the protected/private steps the tests drive one stage at a
time:
allocate_basic_for_testing(),new_step_for_testing(),reset_hessian_for_testing(),save_bfgs_for_testing(),update_inverse_hessian_for_testing(),check_wolfe_conditions_for_testing(),compute_trust_radius_for_testing().Ions_Move_BFGSget_init_done(),get_first_step(),bfgs_routine_for_testing(),restart_bfgs_for_testing().Why references, and why these names
The accessors return non-const references because the tests both seed and
inspect this state. Measured across the two files:
inv_hessalone is 34 readsand 20 writes, and every member except
pos_pis written somewhere, soread-only getters would not have been enough.
T& get_x()matches the convention already used in roughly 83 places in thetree (
get_allocator(),get_bgrids(),get_nonlocal(),get_nproj(), …). Themethod wrappers follow the single existing precedent for a test-only entry point,
set_density_rotations_for_testing()insymm_rotation.h, and both classes carrya comment saying production code must keep calling the protected/private names
directly.
A tradeoff reviewers should weigh
Stating this plainly rather than burying it: this adds 21 public members that
only the tests use, and for the seven method wrappers a public
*_for_testing()forwarder is a weaker boundary than the alternative.That alternative was measured and not taken:
BFGS_Basicalready keeps almostall of its state
protected(pos,grad,move,pos_p,grad_p,move_p,save_flag,tr_min_hit, plus four of the methods), and itsprivate:sectionholds six declarations of the same kind — algorithm state and algorithm steps.
Moving those six into the protected section and letting the two fixtures derive
from the class would have changed no signature and added no public API, at the
cost of relaxing encapsulation toward subclasses. Of the 139 accesses in
bfgs_basic_test, exactly 68 are protected and 68 private, so deriving alonewould not have sufficed without that move.
Both routes were put to the maintainer and this one was chosen. If review prefers
the other, the change is small and mechanical to flip.
Verification
Remote Linux box,
cmake -B build -G Ninja -DBUILD_TESTING=ON -DENABLE_LCAO=ON -DENABLE_MPI=ON -DENABLE_OPENMP=ON:ninja: no work to doon a follow-up full buildMODULE_RELAXbinaries link, includingMODULE_RELAX_bfgs_basic_test,MODULE_RELAX_ions_move_bfgs_testandMODULE_RELAX_ions_move_methods_test(the last one still uses the macro and isunaffected), plus
abacus_basic_paraagent_governance_check.py --base upstream/develop --head HEAD: zeroblockers, no access-hack ratchet finding (4 removed, 0 added), no
PARAM/GlobalV/GlobalCchangeNo production logic changed. No assertion or expected value changed — the
tests call the same code with the same inputs through the new names. No INPUT
parameter or user-facing behaviour changed, so no documentation update is
required.
Not in this PR
ions_move_methods_testkeepssource_relax's last two macros. It reachesthrough
Ions_Move_Methods' aggregated members —imm.bfgs.tr_min_hit,imm.bfgs.save_flag,imm.bfgs.first_step,imm.bfgs.pos/pos_p,imm.bfgs_trad.is_initialized— as well as its ownconverged_,update_iter_and
etot_info_. Note that a derived-fixture approach cannot work there at all:C++ only lets a derived class reach a base's protected members through objects of
its own type, and
Ions_Move_Methodsdoes not derive fromBFGS_Basic. It needsaccessors on
Ions_Move_Methodstoo, and now has theIons_Move_BFGS/BFGS_Basichalf of what it requires from this PR.Continues #7921 / #7949 / #7952 / #7953 / #7963 / #7964 / #7965 / #7966 / #7967 /
#7980 / #7981 / #7982 / #7983.
🤖 Generated with Claude Code