Repository navigation
1D- and 3D-RISM (VV, UV; SCF coupling: EC, RI-EC, cSED), Part B: Equations and Solvers - #26
trajkovski67 wants to merge 33 commits into
Conversation
|
CLA Assistant bot All contributors have signed the CLA. Thank you! |
63c8d97 to
b84b087
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #26 +/- ##
==========================================
+ Coverage 83.12% 83.66% +0.54%
==========================================
Files 176 257 +81
Lines 146308 156368 +10060
Branches 42696 47941 +5245
==========================================
+ Hits 121620 130830 +9210
- Misses 24670 25517 +847
- Partials 18 21 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
I have read the CLA Document and I hereby sign the CLA |
| !> Spectral filter: preconditioner when selected and the Picard fallback filter | ||
| type(moist_model_moz_1d_preconditioner_spectral_type) :: spectral | ||
| !> Small-k damping filter | ||
| type(moist_model_moz_1d_preconditioner_spectral_type) :: damping |
There was a problem hiding this comment.
This is twice the same preconditioner; also, I dont see why these should be part of the uv_problem_type?
There was a problem hiding this comment.
its the same filter type but two different filters, the preconditioner (w0 0.1, also used as the picard fallback) and the damping of picard/diis updates (min weight 0.15). moved both out of uv_problem_type, the 1d model owns them now in solver_filters.f90 and lends them to the solver for each solve (173d40b)
| !> Nonlinear solver | ||
| class(moist_model_moz_solver_type), allocatable :: solver | ||
| !> Independent transpose Krylov controls, copied from the model parameters | ||
| integer :: adjoint_restart = 50, adjoint_max_krylov = 50 |
There was a problem hiding this comment.
same as in 1d, removed from uv3d_problem_type too (5c76ee7)
…aration work (#19) ### Major new Features - **MOZ model family (molecular Ornstein-Zernike; groundwork for 1D-/3D-RISM)**, in `model/moz/` - `model_moz_1d_type` (radial `rgrid`/`kgrid` pair), `model_moz_3d_type` (copy of a 3D volume grid, point grids only) and the bulk-solvent `solvent_vv_type`; Fortran only for now, not yet in C/Python - This PR holds the infrastructure: grids, transforms, potentials, coupling. The OZ/RISM equations and solvers follow in [Part B (#26)](#26); RI-EC and cSED coupling are not part of this PR. Until then `get_energy/response/gradient` and `solvent%solve` refuse with "not implemented" - **MOZ potential layer**: one `moz_potential_type` serves both `solvent%potential` (VV) and `model%potential` (UV) (`model/moz/potential*.f90`) - Ordered list of terms: the first term covering an atom supplies its LJ data and charge (never summed); an atom no term covers is a named error - Lorentz-Berthelot or geometric mixing; Ng split of the Coulomb tail (default on, alpha = 1/bohr) - `compute` builds the short-range plus long-range Ng tables: 1D site-pair tables on radial grids, 3D UV tables on volume grids, phased by the grid's `get_kref` - `compute_adjoint` sends field and charge adjoints back to each contributing term; 3D has full reverse mode (solute atoms, grid points, phase reference) - Electrostatic terms: - `monopole_type`: host atomic charges through the new `atomic_charges` request - `ec_charges_type`: EC tail charges fitted matrix-free to the full host potential. Exact total charge, reference restraint, bounds, smooth nuclear exclusion; fit adjoints go to the potential, nuclei and grid - Solvent table charges: Hirshfeld/RESP/MBIS/CHELPG × gas/solvent/conductor, and SPC/E - `new_fixed_charges` - `multipole_type` is a stub (errors at `update`) - Lennard-Jones terms: GAFF2 and OPLS-AA with atom typing; UFF, DREIDING and transition metals per element; modified SPC/E water; custom LJ. Provenance and licenses are in `lj/PROVENANCE.md` and `THIRD_PARTY_NOTICES.md` - **New grid layer for MOIST** (`math/grid/{radial,angular,atomic,3d}/`) - Radial: Chebyshev-2, midpoint and Gauss-Legendre rules × linear, Becke, HandyMod and Mura-Knowles mappings; uniform r/k pairs for 1D-RISM - Radial r <-> k Fourier-Bessel transforms: DST-IV on uniform pairs (exact inverse to round-off) or dense quadrature for any pair, with adjoints and batched variants - Angular: Lebedev grids with weights summing to 4π. Requests by exact size or by minimum degree/points with a soft target; negative-weight Lebedev sizes can be excluded - Atomic grids with constant, sectored or arc-spacing shell policies, and per-element recipe overrides - `moist_math_grid_3d_type` abstract volume grid: `integrate`, `measure`, analytic volume gradient, Hessian-vector product and Hessian, and a `volume_adjoint_type` with `w_xyz`, `w_w`, `w_xi` and `w_kref` channels - **Cartesian grid** (3D-RISM): box follows the solute centroid; r2c/c2r FFTs - **Atom-centred molecular grid**: Becke, SSF and C∞ power-Voronoi partitions (generated kernels with first and second derivatives); FINUFFT type 1/2 transforms on a dual k-lattice, with a type-3 fallback - Phase-reference contract: every transform measures phases from `get_kref()` - **FFT and NUFFT backends, vendored with no external dependency** (`math/fft/`) - ducc0 FFT subset (`ducc0_0_39_1`, BSD-3, unmodified) behind an `extern "C"` shim `ducc0.cpp` with `bind(C)` Fortran interfaces in `moist_math_fft` - c2c/r2c/c2r 3D, batched and single-axis passes, and DST-IV; plan-free, and no C++ exception reaches Fortran - FINUFFT `0e9c104` (Apache-2.0) on ducc0 (no FFTW, CPU only) plus xsimd `6842624` (BSD-3), built by meson/CMake with no network access - Two OpenMP fixes for nested teams, marked "moist patch" - Threads run on the caller's OpenMP team (no second pool); the thread count comes from the grid or context, never from global state - **Coupling build phase and new host requests** (`channels/coupling.f90`, `channels/response.f90`) - New phase `build`: `prepare_build` -> host answers -> `build` on cavities and models; build answers survive energy staging - New requests: `nuclear_charges` (`z`), `density_matrix` (`dcart`), `atomic_charges` (`q`), `atomic_multipoles` (`q`, `mu`, `theta`), `radial_potential` (`phi`) - New response items `atomic_charge_adjoint`, `atomic_multipole_adjoint`, `radial_potential_adjoint` - One field type (`channels/fields.f90`, formerly `cavity/fields.f90`) for every named-array channel: cavities, response items, request inputs, model domains (`moist_get_model_field_*`) and per-component energies (`moist_get_model_component_*`) - **Run context as a public handle**: `moist_new_context(err, nthreads, verbosity, debug)` / `moist.Context(...)` - Every model requires one; cavities and components may have their own or inherit their model's - MOIST never changes global threading: thread count fixed at construction (0 = `omp_get_max_threads`), applied through `num_threads` clauses only; one context per thread ### Other new Features - **Isodensity cavity** - Density arrives in the build phase as `density_matrix`, replacing the `set_isodensity_density` calls - New `basis = cartesian | spherical` option; spherical densities are checked to lie in the spherical subspace; gemm-based contraction - ECP core fill (`isodensity/core.f90`, `core_fill`, `core_radius_fraction`): a bump fills the core of each nucleus whose host charge z is below Z, so the surface does not collapse into an ECP hole. Uses the host's `nuclear_charges` - **PySCF uses the internal isodensity backend**: `PySCFHost` answers `dcart` and `z` from its own AO basis (libcint coefficients); host work is timed under `Host` - **PCM nuclear charges** read from the host `nuclear_charges` request (respects ECP z_eff) instead of `mol%num` - **Host timers**: `start_timer` / `stop_timer` in Fortran, C and Python (`with ctx.timer(name):`) under a top-level `Host` node; new `Build` timer node; `moist_print_context_timer[_simple]` - **Settings and radii printouts**: cavities and components print their names, description, inputs and parameters on request - `print_parameters` / `print_radii`; `moist_get_model_parameters_text` / `moist_get_model_radii_text`; Python `print_parameters()` / `print_radii()` - Parameter tables carry a print level (2 = normal, 3 = detailed) - **Context warnings** (`ctx%warning`, always to stderr) and **verbosity ladder 0-6**: silent / minimal / normal / detailed / solver summary / solver trace / debug - **GOSTSHYP options** `moist_gostshyp_options` / `GOSTSHYPParameters`: `regularization_start`, `regularization_end`, `suppress_negative_amplitudes` - **Solvent data**: - Partial charges for 186 solvents (Hirshfeld/RESP/MBIS/CHELPG × gas/solvent/conductor, wB97M-V/def2-TZVPP) and MBIS multipoles up to octupole (`solventcharges.inc`, `solventmultipoles.inc`) - 42 placeholder solvent geometries replaced - Gauss radii reachable from the radii factories (`"gauss"`, `moist_new_gauss_radii`, Python `GaussRadii`) - DROP optional-property flags `do_grid_density`, `do_curvature`, `do_normal`, `do_r_iI`, `do_rho` (`do_fine` switches all on) as parameters - Boys functions moved to a shared `moist_math_boys`, used by the PCM A-matrix and the Ng kernel - CLI `--profile`; `CITATION.cff` and `moist --citation` ### Improvements - **Model hierarchy**: `solvation_model_type` is now a real shared base above the continuum and MOZ families - The base owns the context, the lifecycle flags, the coupling registry and the build phase - `model_continuum_type` (formerly `solvation_model_general`) owns cavity and components and stores each component's energy - **GOSTSHYP rework**: - Unit-integral Gaussians - The hard `overlap_floor` cutoff is replaced by a smooth C∞ regularization - Narrow points are switched off by a C2 area polynomial between ω = 1e3 and 1e4 bohr⁻² - **DROP floating-point stability**: - `hypot` eigenvalue gaps instead of `tr²/4 - det` - Curvatures from the tangent-frame shape operator, with a guard for degenerate gaps - `erfc` forms instead of `1 - erf` - **Cavity settings** live in parameter types (`%param`) for iSwiG, NUMSA and MC; nothing prints by default at construction or update - Angular weights sum to 4π in DROP, iSwiG and NUMSA (totals unchanged) - PCM, DROP and MC threading through `num_threads(ctx%get_num_threads())` - Grid and transform lifecycle guards: - Failed `update` sets `natom = 0`, and contractions refuse until a successful update - Molecular-grid period guard and Cartesian box-fit check - Stale-geometry stamp on prepared transforms - Trampolines removed: `lsmr`, `fmin` and the nlesolver line search take functor types. A debug build fails CI on `-Wtrampolines` - Clearer errors: - A rejected response request leaves the response untouched - Every `update` drops the previous surface; the C surface readers (`moist_get_cavity_sizes`, `moist_get_cavity_results`, cavity and anchor gradients, `moist_assemble_amat`, `moist_get_cavity_gaussian`) and the Fortran cavity gradients refuse an unbuilt cavity - `lebedev_order_from_num` and iSwiG report errors instead of writing to `error_unit` - `MOIST_FIELD_MAX_RANK` 2 -> 3, so `w_hess_rho` and `dg_dtheta` report their full shape - Docs: - New pages: context, fields, grids, MOZ, MOZ potential, integrations, bibliography - Reworked C/Fortran/Python/PySCF/coupling references - New logos and DROP figures - Installation and README rewritten for C/C++17 + CMake 3.25 ### Tests - Unit suites 40 -> 69 (meson, `main.F90` and CMake lists agree) - New: 8 MOZ suites, 16 math FFT/grid suites, `cavity_reference`, `cavity_surface_adjoint`, `cavity_drop_projector`, `cavity_drop_properties`, `math_packed_sym`, `utils_prettylistprint_threaded` - Split up: `math_grid` - Renamed: `cavity_drop_lsf_golden` -> `_reference`, `model_general` -> `model_continuum` - MOZ: - FD checks of kernel adjoints, the Ng phase convention and the split identity - EC fit adjoint and active-constraint FD - GAFF typing on 7 solvents; OPLS-AA rules - Grids: - FD gradients and Hessians on all four 3D grid variants - NUFFT type 1/2 vs type 3, and NUFFT vs Cartesian FFT - Partition derivatives, integration, translation/rotation invariance, threaded paths, guards - DST-IV pinned to the RODFT11 convention - New `test_selected` mechanism (meson and CMake): selected tests run as separate `tester <suite> <test>` targets, outside test-drive's OpenMP team, so the threaded ducc0/FINUFFT paths are really exercised. 103 registered unit targets - Isodensity: 28 new tests (core fill, build lifecycle incl. refused gradients of an unbuilt cavity, spherical GTO); new spherical density fixtures - `cavity_reference`: per-point regression for iSwiG, NUMSA and DROP; new translation-invariance tests for PV and marching cubes - API and channels: - `test_api` 34 -> 47 and `test_channels` 40 -> 70: context, build phase, new requests and adjoints, field getters - C/C++ examples extended (`example.c` +1.2k lines) - Python: pytest functions 151 -> 210; new `pyscf_ecp` (core fill on a pseudo-atom and all-electron AuH) - FD hardening in the DROP gradient tests; six-point stencil helper in the CPCM tests - Mutation-testing pass: tighter thresholds and new cases for the DROP projector, properties, filter and LSF, the radial grids and transforms, partitions, 3D volume derivatives (Hessian columns and `should_fail` guards), MOZ EC/LJ/OPLS-AA and parameters - Validation: 103 unit + 2 C/C++ API meson targets, 15 Python/PySCF targets; CI green on `0da3c71`. Covers Linux x86_64/arm64, macOS arm64 and Windows; GCC 14 (debug, bounds checks) and 15, ifx 2025.1, conda and MSYS2; meson and CMake; ILP64; Python 3.10/3.13 with PySCF 2.13.1; wheels; coverage (vendored solvers and FFT backends excluded in gcovr and Codecov); static-release dry run ### Bug Fixes - **Missing volume-origin motion**: the divergence-theorem volume now uses `(r_i - c)·n_i` with the sphere centroid `c`, and `dV/dc` reaches every atom - Fixed in the DROP forward and reverse paths, iSwiG and MC; NUMSA has no volume - New `w_c` channel in `cavity_surface_adjoint_type` - PV volume relative to the cavity volume origin, with the `w_c` adjoint (translation dependence in the gradient) - Marching cubes: - Translation invariance: the tetrahedron apex is the mean atomic position, not the absolute origin - `collapse(3)` -> `collapse(2)` - Wendland C6 1D and 2D/3D derivative formulas corrected - `eig_2x2_symmetric` cancellation near degenerate eigenvalues (`hypot` discriminant) - Uninitialized `z_seed` in the level-6 Newton deflation (introduced in #12) - Projector: - Early return on `abort_error` - `is_kkt` set when the LSF gradient is degenerate - Stale `*_rA` arrays cleared on re-projection; six missing deallocations in the finalizer - Threaded prettylistprint race: gfortran static string-length temporary in deferred-length function results - The context no longer changes the global OpenMP state, so it can't clobber a host's `omp_set_num_threads` - Newton stop code 4 now raises an error instead of printing and continuing - Custom BLAS link probe; radii print bug ### Removals - fpm support (`fpm.toml`, `build_info.f90` fallback, CI legs, docs) - `model/general.f90`, `model/component*`: moved to `model/continuum/` - DROP request type (`drop/request.f90`, `%properties(...)`): replaced by parameter flags - Old grids `math/grid/{molecular,becke,radial}.f90`: `molecular_grid_type`, `new_molecular_grid[_uniform]`, `default_grid_sizes`, `becke_weights`, `chebyshev2_radii` - C API: - `moist_model_options` / `moist_init_model_options` - `moist_set_isodensity_density` / `moist_set_model_isodensity_density` - `moist_get_coupling_request_width`: use `moist_get_coupling_request_field_real(..., "width")` - `moist_get_response_array`: use `moist_get_response_field_real` - Python: - `InternalDensity`, `ModelParameters`, `SolvationModel.parameters` - `CavityField` -> `FieldInfo` - `GaussianMoments.inactive_count` / `.effective_volume` - Context: `set_num_threads`, `writes`, and the pin/restore logic. Radii: the `verbosity` argument - `libgcc_eh` linking; gnu13 and macOS 14 CI legs; `THIRD_PARTY_LICENSES.md` (now `THIRD_PARTY_NOTICES.md`) ### Breaking Changes - **Build requirements**: - A C and a C++17 compiler are now always required, and libmoist links the C++ runtime - OpenMP builds need C++ OpenMP; Apple Clang lacks it, so use GCC, conda clang with llvm-openmp, or `-Dopenmp=false` - CMake >= 3.25 - toml-f is a direct dependency; mctc-lib v0.6.1; test-drive from upstream `791484a`; fclap pinned to `v0.1.0` - Oldest tested compilers: GCC 14 and ifx 2025.1 - **Context required**: - `moist_new_model(err, context, cavity)`, `SolvationModel(context, cavity, components)` and `moist_for_scf(..., context)` - Cavity and component constructors take an optional context - Fortran `ctx` moved from the second positional argument to an optional trailing argument - **Isodensity is not complete after `update`**: hosts must run `prepare_build` -> answer -> `build` after every update and every new density - With the default `core_fill = .true.`, hosts must also answer `nuclear_charges` - Between `update` and a successful `build` the cavity holds no surface and its readers refuse it. `moist_prepare_model_build` unbuilds the model; a model-owned cavity keeps its last built surface until the model's `build` replaces it - **PCM gradients require `nuclear_charges` `z`** from the host (no fallback to atomic numbers) - **Struct layouts changed in place under `V_1_0`**: - `moist_drop_options` / `moist_iswig_options` lose `debug` and `verbosity`; `moist_drop_options` / `moist_cfc_options` lose `reserved0` - `moist_isodensity_options` gains `basis`, `core_fill` and `core_radius_fraction` - `moist_new_gostshyp_component` takes `context` and options - **Renames**: - `solvation_model_general` -> `model_continuum_type` (`new_continuum_model(self, ctx, cavity, error)`) - `solvation_model_component_*` -> `model_continuum_component_*` - `moist_model_component*` -> `moist_model_continuum_component*` - `moist_cavity_fields` / `cavity_field_*` -> `moist_channels_fields` / `field_*` - `moist_math_grid_lebedev` -> `moist_math_grid_angular_lebedev` - Response item `gostshyp_amplitude` -> `gaussian_amplitude` - `MOIST_FIELD_MAX_RANK` 2 -> 3: hosts must size `dims` buffers for rank 3 - **GOSTSHYP numerics**: normalized Gaussians, so host moment and integral conventions must match; energies differ near the old floor - **Defaults**: - Verbosity: library 1 -> 2, CLI 2 -> 4 - `debug` raises verbosity to 6 - Profiling off unless `--profile` - Molecular grids default to a bounded HandyMod/Lebedev-110 recipe (old per-element defaults via `default_element_recipes`) - **Cavity subclass contract**: - `get_surface_gradient` is `non_overridable`; override `contract_surface_gradient` instead - `update` and `build` must call `update_cavity_base` / `build_cavity_base` first - `solvation_model_type` gains the deferred `atom_count` and `declare_pass` - **Other**: - `ang_weight` members sum to 4π; iSwiG/NUMSA/MC settings moved under `%param` - `moist_get_cavity_gradient` refuses buffers for outputs that were not computed (`do_fine`) - Release tags must match `v*` This PR summary was generated and written together with Claude.
| if (allocated(error)) return | ||
| end do | ||
| end subroutine test_jfnk_nonlinear | ||
|
|
There was a problem hiding this comment.
Reads workspace scratch directly (i.e. changes if we change ws layout)
There was a problem hiding this comment.
the workspace has getters now (field_shape, capacity, basis_vector ...) and the tests only use those, no direct scratch reads anymore (b5c7fae)
| call check(error, all(solver%workspace%ws_v == 0.0_wp) .and. .not. allocated(solver%workspace%ws_f_pert)) | ||
| if (allocated(error)) return |
There was a problem hiding this comment.
Reads workspace scratch directly (i.e. changes if we change ws layout)
There was a problem hiding this comment.
the workspace has getters now (field_shape, capacity, basis_vector ...) and the tests only use those, no direct scratch reads anymore (b5c7fae)
Keep the solvers, all settings with defaults as one table, one usage example, response solves, preconditioner choices and warnings; drop the per-method API notes, solver internals and the test overview. The example now uses the actual new_solver signature, and the 1D VV preconditioner and deflation defaults are stated.
The docs no longer compare against pysofft or cite moist_dev commits; options are described by what they do. Source comments drop the moist_dev/pysofft provenance lines. The pysofft_reference tests are now reference_values and name moist_dev only as the source of their numbers.
The workspace type reports its field shape, restart and recycle capacities, finite-difference scratch, basis vectors and allocation addresses, so the solver tests no longer read the scratch arrays. The happy-breakdown test gets its stale basis vector from a first solve instead of writing it.
Response solves borrow whichever solver ran the primal solve, so the transpose Krylov controls now live on the base solver type instead of the 1D and 3D UV problem types. The parameter type moves to solver/core and the group from the JFNK controls to the solver parameters, keeping the jfnk.adjoint keys; new_solver sets the single copy for every solver kind. solve_adjoint takes explicit parameters, else the owner's, else defaults. The explicit default constructor Intel needs moves with the component, and the VV solver default passes it to the outer constructor.
The spectral preconditioner, the Picard fallback filter and the small-k damping move from the UV problem type into a filters type that the 1D model builds with new_uv_filters and lends to solve and response. The problem attaches them to its solver for one solve and detaches them after; damping applies only to updates the solver allows to post-process. Both entry points refuse filters that are not set up or belong to another radial grid pair. Tests cover the refusal, Picard damping and the attach and damping paths of the filters.
The JFNK module re-exports the adjoint parameter type again, so code importing it from there keeps compiling after the type moved to the solver core. The JFNK workspace stamps each allocation with a value unique across workspaces; the 3D SCF table test compares stamps, which also catches storage cleared and reallocated in place or at the same address. The solver docs now say which fallbacks of a warm start are reported and which fall back to the cold guess silently.
… input handling to library
1f143dd to
787f102
Compare
- Checkpoint format 2: every grid describes itself by a layout (Cartesian box, radial nodes or points), and states store the grid points and the long-range tail - Grid transfers: copy of coinciding points, identity on atom-centered grids, Fourier and trilinear interpolation from Cartesian boxes, moving least squares from point grids, spline on radial nodes, after a rigid fit of the solute; uncovered points start cold - moz_blowup moves a field between two grids; moz_checkpoint_guess moves a state the caller keeps - Memory checkpoint store; stores skip candidates that cannot seed the solve - Safe saves: exclusive temporary files and replacement in one step, also on Windows and for concurrent jobs; binio guards against reopening and bad extents - A solve that stops on a closure switch converges only on the requested closure - FINUFFT execute is called through its C binding - gfortran 13 fixes; docs and tests for all of the above
Add the Cartesian auxiliary-space preconditioner for 3D UV, including nested lattices, flexible GMRES, model-owned workspaces and input settings. Strengthen checkpoint, grid-transfer and solver tests and share fixtures. Validation: gfortran 13 debug 247/247; gfortran 15.2 debug 246/247. The unchanged parameter-file test also reproduces the Jonquil recursion check failure at 59e3a30. Nine retained regression mutations are caught.
Conflicts resolved: - docs/models/index.rst: main's links with the PR's directory list (moz/solvent, 1d UV only, closure and solver) - docs/models/moz/potential.rst: main's link to the solvent charges with the PR's coulomb_spce/coulomb_cspce row - potential/electrostatic/solvent.f90, potential/lj/solvent.f90: main's explicit pair=null() also on the PR's coulomb_cspce and lj_cspce - test/unit/test_parameters.f90: kept the PR version
…are fused - test_fit_rotation sets every component of the hand-built map instead of resetting it with an empty constructor, which Intel Fortran refuses for the array default of rot - test_kdtree_brute compares the neighbours by their brute-force distance within a relative 1e-12, and checks the tree's own order: no repeats, equal distances by index
- test_moz_solvent and test_moz_potential_electrostatic add the library's coulomb_hirshfeld_solvent term instead of a bare structure constructor, which ifx copies into the potential with its allocatable components already allocated - test_moz_checkpoint appends a saved state without an array constructor, on which ifort 2021 aborts, and restarts vv_intermediate from a new store and empty log - test_moz_drism compares to the reference relative to its largest element: one ULP in sin and cos moves zeta by up to 5e-13 through the cancellation in j1(x)/x - moz_1d_uv reference_values solves on one thread; thread_independence covers the threading, and each team barrier is slow when concurrent tests share the cores
This PR migrates the closure and solver work from the development branch into
rism. It adds 1D VV/UV and 3D UV calculations, response and gradient calculations, thermodynamic pressure corrections, and a finite-size box correction, together with the associated input handling, tests, and documentation.