Skip to content

source_basis/module_ao: use the getters that already exist instead of #define private public - #7952

Merged
mohanchen merged 1 commit into
deepmodeling:developfrom
Critsium-xy:refactor/orb-tests-use-getters
Sep 12, 2026
Merged

source_basis/module_ao: use the getters that already exist instead of #define private public#7952
mohanchen merged 1 commit into
deepmodeling:developfrom
Critsium-xy:refactor/orb-tests-use-getters

Conversation

@Critsium-xy

Copy link
Copy Markdown
Collaborator

Third tranche of the #define private public cleanup, after #7940 (tests
driving global PARAM) and #7949 (tests calling private methods). This one
covers the case where the member the test reaches for already has a public
getter
— so the macro comes off with no production change at all.

Three cases, and the middle one is a trap

orb_nonlocal_test.cpp and orb_read_test.cpp reach into Numerical_Nonlocal
and LCAO_Orbitals. Neither file touches PARAM; nothing is added to a
production header. The entire diff is in the two test files.

1. The member is used as a plain value — read it through its getter.
lcao_.kmesh becomes lcao_.get_kmesh() (4 sites); the five SetTypeInfo
assertions become nn.getLabel(), getType(), getLmax(), get_rcut_max(),
get_nproj(). Those already compared against the fixture's inputs, so they stay
real checks.

2. The assertion is EXPECT_EQ(obj.get_x(), obj.x). Substituting the
getter here would produce EXPECT_EQ(get_x(), get_x()) — an assertion that
cannot fail. Mechanically removing the macro this way would leave the test
green and empty, which is worse than the macro. These are re-anchored to the
value the object was actually given.

orb_nonlocal_test already had one line in the right form
(EXPECT_EQ(nn.get_rcut_max(), rcut_max_)); the other four now match it.

In LCAO_Orbitals::Getters, seven of the twelve assertions covered private
members. ntype and lmax are passed straight into Read_Orbitals, so they
anchor to ntype_ / lmax_. The remaining four are derived, and the
production formula is deliberately not restated in the test — a test that
recomputes the thing it is checking passes even when the formula is wrong. They
are asserted as the concrete values this fixture implies, each with its
provenance in a comment:

value where it comes from
kmesh 1113 int(sqrt(ecutwfc)/dk) + 4 = int(sqrt(123)/0.01) + 4
nchimax 2 H is 2s1p, O is 2s2p1d
lmax_d, nchimax_d 2 from jle.orb
rcutmax_Phi 8 au H is 8 au, O is 7 au

Each was measured against the built test, and kmesh additionally cross-checked
by hand against Read_Orbitals so the assertion records the correct value rather
than merely the observed one.

3. The test wrote a private member to build a fixture
nnl[i].rcut = 1.0 in NumericalNonlocalTest::SetUp.
Numerical_Nonlocal_Lm derives rcut from the last point of its radial mesh
(rcut = r_radial_in[nr-1]), so each projector is now built through the public
set_NL_proj() with a minimal three-point mesh whose endpoint is the wanted
rcut. This removes the write and exercises set_NL_proj, which no assertion in
this file reached before.

ecutwfc, dk, dR, Rmax and dr_uniform are public members of
LCAO_Orbitals and never needed the macro. The four that have a corresponding
fixture input are anchored to it for consistency; dr_uniform is left as it was.

Result

Macro occurrences in these two files: 2 -> 0. No #undef private is added,
and no file whose macro survives is touched.

Verification

Linux, cmake -B build -G Ninja -DBUILD_TESTING=ON -DENABLE_LCAO=ON -DENABLE_MPI=ON -DENABLE_OPENMP=ON,
followed by cmake --install build — which matters here: the module_ao tests
get their orbital data from install(DIRECTORY lcao_H2O ...), so without the
install step ORB_read_test fails and ORB_atomic_lm_test /
ORB_nonlocal_lm_test segfault on missing input, both before and after this
change.

  • build: 0 errors (branch and base both BUILD_EXIT=0).
  • MODULE_AO_ORB_nonlocal_test, MODULE_AO_ORB_read_test,
    MODULE_AO_ORB_atomic_lm_test, MODULE_AO_ORB_nonlocal_lm_test: all pass.
  • full unit suite: 2 of 339 fail — the failure set is identical to the base
    commit
    (9cbcc0b55) built, installed and run in a parallel worktree in the
    same environment; both comm directions are empty. The two are
    MODULE_HSOLVER_diago_hs_parallel and MODULE_HSOLVER_LCAO, both
    mpirun-based and pre-existing.
  • agent_governance_check.py: 0 errors. The access-hack ratchet reports
    nothing (2 removed, 0 added) and no PARAM/GlobalV/GlobalC reference is
    added or removed, so the global-dependency budget is untouched.

No INPUT parameter and no user-visible behaviour changed, so
docs/parameters.yaml and docs/advanced/input_files/input-main.md need no
update.

What is not here

The other two module_ao tests, orb_atomic_lm_test.cpp (887 lines) and
orb_nonlocal_lm_test.cpp (755 lines), hold the bulk of this category — roughly
190 further sites where a member with an existing getter is read directly. They
are left for a separate PR because they cannot be finished the same way: each
also calls private methods (cal_kradial, cal_rradial_sbpool, plot,
freemem, renew, get_kradial) and orb_atomic_lm_test reads psir, which
has no getter. Those need a friend grant in a production header — the #7949
pattern — which is a different review question from "use the accessor that
already exists", and mixing the two would obscure both.

Their getter-vs-member block has the same shape as the one fixed here but over
arrays (get_psi() vs psi), where there is no scalar input to re-anchor to;
that block is better routed through the fixture once the friend is in place.

🤖 Generated with Claude Code

…no production change

`orb_nonlocal_test.cpp` and `orb_read_test.cpp` switched off access control for
their whole translation unit to reach members of `Numerical_Nonlocal` and
`LCAO_Orbitals` that mostly already have public getters. Neither file touches
PARAM, and neither needs anything added to a production header: the macro is
removed by using the accessors that exist, plus one fixture built through the
public API instead of by assignment.

Three distinct cases, and the second is the interesting one:

1. The member is used as a plain value. Read it through its getter --
   `lcao_.kmesh` -> `lcao_.get_kmesh()` (4 sites), and the five assertions in
   `SetTypeInfo` now read `nn.getLabel()`, `getType()`, `getLmax()`,
   `get_rcut_max()`, `get_nproj()`. Those already compared against the fixture's
   inputs, so they stay real checks.

2. The assertion *is* `EXPECT_EQ(obj.get_x(), obj.x)`. Substituting the getter
   would turn it into `EXPECT_EQ(get_x(), get_x())` -- it cannot fail, and the
   test would be silently gutted. These are re-anchored to the value the object
   was given instead, which is what `orb_nonlocal_test` already did on one line
   (`EXPECT_EQ(nn.get_rcut_max(), rcut_max_)`); the rest now match it.

   In `LCAO_Orbitals::Getters`, seven of the twelve assertions covered private
   members. `ntype` and `lmax` are passed straight into `Read_Orbitals`, so they
   anchor to `ntype_` / `lmax_`. The other four are derived, and the production
   formula is deliberately *not* restated in the test -- a test that recomputes
   what it is checking passes even when the formula is wrong. They are asserted
   as the concrete values this fixture implies, each with its provenance:
   `kmesh` 1113 = int(sqrt(123)/0.01) + 4, `nchimax` 2 (H is 2s1p, O is 2s2p1d),
   `lmax_d`/`nchimax_d` 2 from jle.orb, `rcutmax_Phi` 8 au (H 8 au, O 7 au).
   Measured against the built test to confirm, and kmesh cross-checked by hand
   against Read_Orbitals.

3. The test *wrote* a private member to build a fixture:
   `nnl[i].rcut = 1.0` in `NumericalNonlocalTest::SetUp`. `Numerical_Nonlocal_Lm`
   derives rcut from the last point of its radial mesh, so each projector is now
   built through the public `set_NL_proj()` with a minimal three-point mesh whose
   endpoint is the wanted rcut. That removes the write and additionally exercises
   `set_NL_proj`, which no assertion in this file reached before.

`ecutwfc`, `dk`, `dR`, `Rmax` and `dr_uniform` are public members of
`LCAO_Orbitals` and never needed the macro; the four that compare against a
fixture input are anchored to it for consistency, and `dr_uniform` is left alone.

Macro occurrences in these two files go 2 -> 0. No `#undef private` is added, and
no file whose macro survives is touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@mohanchen mohanchen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mohanchen mohanchen added Refactor Refactor ABACUS codes The Absolute Zero Reduce the "entropy" of the code to 0 labels Sep 12, 2026
@mohanchen
mohanchen merged commit 9958006 into deepmodeling:develop Sep 12, 2026
17 checks passed
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