solvent_pcm Module

declares the iso_c_binding interfaces to the two production C adapter entry points (source/solvent_ddx_adapter.c) and orchestrates the closed reaction-field loop inside a single SCF Fock build:

D  ->  phi_cav  ->  ddX q_cav  ->  V_pcm  ->  Fock / E_pcm

The C adapter returns status 2 when OpenQP was built without OQP_ENABLE_DDX, so a PCM-enabled run on a non-ddX build aborts here with a clear message rather than silently producing a vacuum result.

SOURCE CONSISTENCY (ddX forward Phi vs adjoint Psi): * Phi (forward solve RHS): the EXACT total solute potential phi_cav at the cavity points, built from the full AO density (electrostatic_potential_ unweighted) plus the analytic nuclear term. * Psi (adjoint solve source): a FULL-DENSITY source. Atom-centered real solid-harmonic multipoles M_lm are accumulated for l = 0..PCM_PSI_LMAX (=8) from the full AO density by numerical quadrature over a dedicated source-projection molecular grid that reproduces the reference ddCOSMO/ddPCM density partition: per-atom (PARENT-ATOM) point assignment with Becke-original (3-iteration) fuzzy-cell weights and Treutler-Ahlrichs sqrt(R_i/R_j) atomic-size shifting over the Becke Bragg-Slater table (H = 0.35 A), WITH the literature outside-sphere leak continuation qrsph^(2l+1)/r^(l+1) for r>rsph (build_full_density_multipoles / pcm_grid_update). The moments are in the exact ddX harmonic convention -- the real-solid-harmonic basis is evaluated by ddX's OWN routines (use ddx_harmonics: ylmscale, ylmbas) so ddX stays an external, dynamically-linked dependency and no harmonic code is vendored into OpenQP; only the interior/exterior leak bookkeeping in pcm_accumulate_leak is OpenQP's. They are then mapped to psi by the ddX rule psi(lm,isph)=4pi/((2l+1) rsph^l) M_lm(isph) using the production cavity radii (oqp_ddx_pcm_radii). The production q_cav is the ddX adjoint charge from oqp_ddx_pcm_solve_psi(psi, phi_cav): both the forward RHS and the adjoint source are full-density quantities. This is recorded by "PCM diag pcm_source_mode=full_density_ multipoles_lmax8_exact_phi" and "PCM diag psi_source=full_density_grid_ multipoles_lmax8_becke3_treutler_parent_atom_leak". * NOTE: the per-sphere moments are partition-defined integrals (Becke-original/Treutler cells), the same convention the reference ddPCM implementations project on their per-atom Becke grids; the two codes agree in the fine-grid limit, with only quadrature-mesh differences remaining (it is NOT claimed to be bit-identical to PySCF's grid-projected psi). * The legacy l<=2 atom-centered Mulliken multipole solve (Phi AND Psi from the l<=2 source) is still run as a DIAGNOSTIC only, to report the source-vs-exact phi residual and the q_cav shift between the old l<=2 psi and the new full-density psi (PCM diag q_cav_vs_rms).

VALIDATED SCALAR CONVENTIONS (analytic Born-ion/ddX oracle gate): * phi_cav sign: phi_total = sum_k Z_k/|r-R_k| + phi_elec * q_cav sign/scale: ddX cavity-projected adjoint charge (ddx_get_xi) used directly as the external-charge vector for external_charge_potential. * E_pcm: -0.5 * dot_product(phi_cav, q_cav), with NO additional dielectric factor: ddX folds the full dielectric response into its ddPCM R_eps operators, so -0.5 = ddx_pcm_energy = the PHYSICAL solvation free energy. Proven by the Born-ion oracle (point charge q centered in a single sphere of radius R): -0.5 reproduces -(1/2)(1-1/eps)*q^2/R to machine precision at eps = 78.3553 and eps = 2. An extra f(eps) = (eps-1)/eps here (as in PySCF's solvent.ddpcm) would double-count the dielectric scaling, by -1.3% at eps=78 and -50% at eps=2. The single canonical runtime path and these conventions are pinned by tests/test_pcm_canonical_runtime_path.py.