Skip to content

source_relax: give BFGS_Basic and Ions_Move_BFGS an explicit test seam, removing four access hacks - #7984

Merged
mohanchen merged 1 commit into
deepmodeling:developfrom
Critsium-xy:refactor/bfgs-basic-accessors
Sep 17, 2026
Merged

mohanchen merged 1 commit into
deepmodeling:developfrom
Critsium-xy:refactor/bfgs-basic-accessors

Conversation

@Critsium-xy

Copy link
Copy Markdown
Collaborator

What this does

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, so the access specifiers mean what they say. Four
macros removed; source_relax drops from 6 to 2.

BFGS_Basic

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(),
compute_trust_radius_for_testing().

Ions_Move_BFGS

get_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_hess alone is 34 reads
and 20 writes
, and every member except pos_p is written somewhere, so
read-only getters would not have been enough.

T& get_x() matches the convention already used in roughly 83 places in the
tree (get_allocator(), get_bgrids(), get_nonlocal(), get_nproj(), …). The
method wrappers follow the single existing precedent for a test-only entry point,
set_density_rotations_for_testing() in symm_rotation.h, and both classes carry
a 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_Basic already keeps almost
all of its state protected (pos, grad, move, pos_p, grad_p, move_p,
save_flag, tr_min_hit, plus four of the methods), and its private: section
holds 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 alone
would 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:

  • configure exit 0; 3354/3354 targets, build exit 0, zero errors;
    ninja: no work to do on a follow-up full build
  • all 15 MODULE_RELAX binaries link, including
    MODULE_RELAX_bfgs_basic_test, MODULE_RELAX_ions_move_bfgs_test and
    MODULE_RELAX_ions_move_methods_test (the last one still uses the macro and is
    unaffected), plus abacus_basic_para
  • agent_governance_check.py --base upstream/develop --head HEAD: zero
    blockers
    , no access-hack ratchet finding (4 removed, 0 added), no
    PARAM/GlobalV/GlobalC change

No 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_test keeps source_relax's last two macros. It reaches
through 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 own converged_, 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_Methods does not derive from BFGS_Basic. It needs
accessors on Ions_Move_Methods too, and now has the Ions_Move_BFGS /
BFGS_Basic half 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

…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 mohanchen added Refactor Refactor ABACUS codes The Absolute Zero Reduce the "entropy" of the code to 0 labels Sep 17, 2026
@mohanchen
mohanchen merged commit 4a9aee7 into deepmodeling:develop Sep 17, 2026
17 checks passed
Zanthoxylum pushed a commit to Zanthoxylum/abacus-develop that referenced this pull request Sep 17, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Refactor Refactor ABACUS codes The Absolute Zero Reduce the "entropy" of the code to 0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants