Skip to content

Feature: periodic self-consistent continuum solvation (SCCS) last step: solvent-aware cavity - #8252

Merged
mohanchen merged 13 commits into
deepmodeling:developfrom
LKFEIYI:codex/sccs-pcc-4-sa
Nov 5, 2026
Merged

mohanchen merged 13 commits into
deepmodeling:developfrom
LKFEIYI:codex/sccs-pcc-4-sa

Conversation

@LKFEIYI

@LKFEIYI LKFEIYI commented Nov 1, 2026 •

Copy link
Copy Markdown

Linked Issue

Last part of #8052. It follows #8075 (PCC), #8162 (SCCS Step 1), #8194 (Step 2: PCC 0D/2D), #8219 (Step 3: lowpass filter and full cavity) and #8222 (Step 4: SCF integration).

What's changed?

The SCCS cavity of Steps 1–4 is a local function of the density. Gaps between atoms that are too narrow for a solvent molecule are still counted as solvent, so the dielectric reaches into them. This PR adds Environ's solvent-aware interface (Andreussi et al., J. Chem. Theory Comput. 15, 1996 (2019)), which fills those gaps with solute:

  • a probe sphere of radius sccs_radial_scale * sccs_solvent_radius is convolved with the local boundary s, which gives the probe solute fraction c = p * s;
  • points where c exceeds sccs_filling_threshold are filled: s_sa = s + (1 − s) f(c), with f(c) = 1 − erfc((c − t)/Δ)/2.

The energy follows Environ, and ΔG agrees with QE-Environ to 0.00 meV for periodic water, with and without filling.

Differences from QE-Environ

# Topic QE-Environ 3.1.1 ABACUS (this PR) Advantage Cost
S1 Probe in a periodic cell Minimum image: the probe is truncated in cells narrower than 2(R + 5σ) Sum over every periodic image within the cutoff The filling does not depend on the cell size. In wide cells it is identical to Environ's None found
S2 Surface potential of the filled boundary Continuum curvature with the density Hessian. It diverges as −tr H/r where ∇s_sa → 0 inside the filling step Exact derivative of the discrete chain surface and volume Bounded at density saddles, and the derivative of the energy actually computed None found
S3 PCC dielectric without lowpass FFT derivatives of s_sa Chain formula on the FFT derivatives of the local s The FFT derivatives of the sharp s_sa ring where the filling switches within a grid spacing Applies only without lowpass. With lowpass, ABACUS takes Environ's filtered FFT derivatives of s_sa, which cause the coarse-grid limitation below

S1, narrow cells. A water chain in an 8-bohr cell against its 16-bohr supercell, with sccs_solvent_radius 3:

  • QE-Environ fills 8.6 against 39.6 bohr³ per molecule, a cell dependence of 10.6 meV per molecule;
  • ABACUS gives the same result in both cells.

WideCellProbeMatchesEnvironMinimumImageProbe checks that the probe equals Environ's minimum-image probe in a wide cell.

S2, density saddle. He₂ with the midpoint saddle on a grid node (12 bohr, ε = 1, γ = 50 dyn/cm):

  • QE-Environ is singular there even without filling: 4 of 6 SCFs (chain and fft, with and without SA) do not converge in 200 steps.
  • ABACUS with Environ's curvature formula diverges and stops at step 133.
  • ABACUS with the exact derivative converges in 6 steps.
  • ChainSurfaceDerivativeStaysBoundedAtADensitySaddle covers this case.

S3. The spectral gradient of s_sa made the surface too large for f₀ < 0.75 at the default sccs_filling_spread 0.02. The chain gradient reproduced QE. The same ringing enters the dielectric when s_sa is differentiated on the unfiltered grid.

Validation

Against QE 7.6 + Environ 3.1.1. Abacus: PW, 100/400 Ry, ONCV PBE, 8 MPI × 1 OpenMP, scf_thr 1e-10.

Case ΔG ABACUS (eV) ΔG QE (eV) ΔΔG (meV)
H₂O periodic, r = 2.6, t = 0.825 (no filling) −0.27817 −0.27817 −0.00
H₂O periodic, r = 2.0, t = 0.3 (filled 21.3 bohr³) −0.19204 −0.19204 −0.00
  • Surface and volume agree to 0.01–0.02 bohr² and bohr³.

  • Analytic − FD force (y on H1, ±0.005 Å) for the filled case:

    • ABACUS: −3.6e-4 eV/Å;
    • QE chain: −1.6e-3 eV/Å.

    Both use the continuum electrostatic cavity potential without the lowpass.

  • Water dimer (25 bohr, 75/300 Ry, pre-split implementation): ΔE_SA agrees to ≤ 0.13 meV of 17–37 meV in the ε = 1, periodic and PCC0D stages. The split layer reproduced that implementation to ≤ 2.7e-10 eV.

Force consistency.

  • Setup: water monomer, water-neutral, r = 2, t = 0.3, lowpass 10/5; first H along x ±0.01 bohr, scf_thr 1e-10.
  • Each point: OMP_NUM_THREADS=1 mpirun -np 4 abacus; FD = −(E(x+h) − E(x−h))/2h with h = 0.01 bohr.
  • Values: solvation part of analytic − FD, in eV/Å, that is, analytic − FD minus the same for imp_sol 0 on the same grid.
Geometry, grid no SA, no lowpass no SA, lowpass SA, no lowpass SA, lowpass
PW PCC2D, 20/240 Ry +1.2e-1 +5.7e-5 −3.1e-3 −3.6e-5
PW PCC0D, 20/240 Ry +6.1e-2 −1.8e-5 −3.1e-3 +5.0e-5
PW periodic, 20/240 Ry +1.3e-2 – −5.7e-3 –
LCAO γ PCC2D, ecutwfc 60 (ecutrho 240) +2.5e-3 +5.0e-5 −6.5e-4 +5.0e-5
  • With filling, out_sol 1 writes sol_cavity.cube as the filled boundary s_sa, and adds sol_cavity_local.cube (s) and sol_filled_fraction.cube (c).
side_all

Unit Tests and/or Case Tests for my changes

Tested commit: 91424c0, three commits on develop d20090c. GCC 11 + OpenMPI 5.0.10 with MPI, OpenMP, LCAO, ELPA, LibXC and LibRI; OMP_NUM_THREADS=1 unless noted.

ctest --test-dir build -R "sccs|pcc|surchem|cal_vel|cell_tools|potentials_new|MODULE_CHARGE_|MODULE_IO_(availability|input_domain|input_help_test|input_test_para|parse_args|read_input_serial|read_input_tool|read_item_serial)"
    # 44/44 passed, incl. MODULE_HAMILT_sccs_poisson_mpi (2 ranks), _empty_partition_mpi (3 ranks) and _omp4 (4 threads)
cd tests/<dir> && bash ../integrate/Autotest.sh -a build/abacus_std_para -n 4 -o 1 -j 4 -r '^(scf_sol_H2|scf_sol_H2O|scf_sol_H2O_out_sol|scf_sccs.*|scf_pcc.*|scf_solvation|relax_sccs.*)$'
    # 01_PW 72, 02_NAO_Gamma 43, 03_NAO_multik 12 checks passed (on a5cfb0963; 91424c05f changes only help texts)
python3 tools/03_code_analysis/agent_governance_check.py --base upstream/develop --head HEAD   # 0 errors, 13 warnings
  • New unit tests: test_sccs_sa_probe.cpp, test_sccs_sa.cpp, test_sccs_thread_sum.cpp, and new cases in test_sccs_cav_deriv.cpp, test_sccs_functional.cpp, test_sccs_lowpass.cpp, test_sccs_ion_force.cpp, test_sccs_response.cpp, test_pot_sccs.cpp, test_sccs_output.cpp and test_read_inp_sccs.cpp. No existing assertion is changed.
  • New integration cases: 01_PW/scf_sccs_sa, 01_PW/scf_sccs_sa_pcc_2d_lowpass and 02_NAO_Gamma/scf_sccs_sa_pcc2d, each checking sccs_filled_volumeref.
  • Not run: the full integration suite; GPU builds. The force and QE tables were not rerun on 91424c0.

Governance Notes

  • INPUT/docs changes. Five new keys: sccs_solvent_radius, sccs_radial_scale, sccs_radial_spread, sccs_filling_threshold and sccs_filling_spread, all with Environ's defaults. No documentation update required: docs/parameters.yaml and input-main.md are generated in CI from the Input_Item descriptions.
  • Core module impact.
    • Hamilt (module_sccs): new sccs_sa.cpp/h and sccs_thread_sum.h. make_cav_derivs, solve_response and eval_functional gain a probe_kernel parameter, and every call site is updated.
    • ElecState: PotSccs, SccsFixedSources and SccsResume carry the probe kernel. SccsOutput reports the filled volume.
    • No changes to ESolver, HSolver, Operator or Psi.
  • Global dependencies. None added: the probe kernel is passed explicitly from SccsFixedSources to the solver and the functional.
  • Exceptions requested: none. The governance check reports 0 errors and 13 warnings:
    • 12 header-dependency warnings.
      • sccs_sa.h in sccs_response.h: SccsResponse holds a FilledCavity by value.
      • vector3.h and <vector> in sccs_sa.h: members of FilledCavity.
      • <array>, <cstddef>, <vector> and <omp.h> in sccs_thread_sum.h: a header-only template.
      • Test headers sccs_sa_test.h and sccs_test.h: fixtures.
    • 1 documentation-sync warning, reported only in the local run, which has no PR body. In CI the INPUT/docs statement above clears it.
  • auto. Outside tests, auto appears only in the nine grid sums, as const auto add_... = [...]. A lambda's closure type cannot be written out, and std::function would add an indirect call per grid point in these hot loops. Every other new local has an explicit type.
  • Code quality score. Every changed file passes, except read_inp_out.cpp, which scores 0 on develop too (1830 lines; item_output has cyclomatic complexity 178). This PR changes only the out_sol help text there.

@ABACUS-PR-BOT ABACUS-PR-BOT 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.

Static review of 6f4fc718 found no confirmed implementation defect. The comments cover INPUT guidance, OpenMP test coverage and small governance cleanups. No build, runtime or CI verification was performed.

Non-blocking: Please add or link the exact validation commands, outcomes and tested commit in the PR description, including the selected unit/integration tests and force finite differences. The setup tables do not identify those invocations; keep the already-labeled pre-split results separate.

- sccs_lowpass_p1, sccs_solvent_radius: state how the filled dielectric
  is differentiated with PCC with and without lowpass.
- sccs_filling_spread: document the filling erfc(t/spread)/2 at zero
  probe fraction.
- Add thread_sums and repeated-solve tests, run with 4 OpenMP threads.
- Name call arguments (rule 14).

@ABACUS-PR-BOT ABACUS-PR-BOT 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.

The new OpenMP test entry can collide with the normal entry's warning log during parallel CTest execution. The earlier INPUT-guidance and validation-command requests are addressed; two narrow test/style follow-ups remain in their existing threads.

Static incremental review of 91424c05 from 6f4fc718; no build, runtime or CI verification was performed.

LKFEIYI and others added 5 commits November 2, 2026 16:35
- The four MODULE_HAMILT_sccs_poisson entries share a resource lock: they run
  in one directory, where a test writes and removes its warning log.
- The omp4 entry and ThreadCount turn off dynamic teams; ThreadCount restores
  the previous setting.
- Name the potential difference before std::abs (rule 14).
Comment thread tests/01_PW/scf_sccs_sa/INPUT Resolved

@ABACUS-PR-BOT ABACUS-PR-BOT 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.

Two non-blocking documentation follow-ups after the merge. Static review only; no builds or tests were run.

Only out_app_flag 2 adds the g suffix; mode 0 overwrites the fixed name.
@mohanchen

Copy link
Copy Markdown
Collaborator

Great! Thanks for your contributions.

@mohanchen
mohanchen merged commit e58ffe5 into deepmodeling:develop Nov 5, 2026
18 checks passed
@mohanchen mohanchen added Features Needed The features are indeed needed, and developers should have sophisticated knowledge Refactor Refactor ABACUS codes Tests/Examples Issues/PR related to unit tests and integrate tests labels Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Features Needed The features are indeed needed, and developers should have sophisticated knowledge Refactor Refactor ABACUS codes Tests/Examples Issues/PR related to unit tests and integrate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants