Pick The Entry Point¶
Need |
Entry point |
Input |
Output |
|---|---|---|---|
Repeated RPC or optimizer steps |
|
one
|
serialized
|
Repeated native C forces |
|
one
|
Hartree plus Hartree/Bohr forces |
Single serialized calculation |
|
|
serialized
|
Scalar compatibility call |
|
C coordinate
arrays plus
|
Hartree plus Hartree/Bohr forces |
Capability discovery |
|
stable feature ID |
|
Use the session result path for new drivers. It keeps method setup and
topology state in one object while preserving the same
PotentialResult carrier used by RPC frontends.
The compatibility array calls are useful when a host already owns native C arrays and only needs Hartree/Hartree-per-Bohr values. The serialized result calls are the better boundary for RPC frontends, cross-language bindings, and drivers that already use the shared OmniPotentRPC schema.
C ABI¶
Include cpmdc.h and pass serialized Cap’n Proto message bytes to the
ABI functions. Wire structs are generated from
schema/Potentials.capnp; the header does not redefine CPMDParams
fields as a second C struct language.
#include <cpmdc.h>
#include <stdlib.h>
int rc = cpmdc_set_params(params_bytes, params_size);
CPMDCResult result = cpmdc_energy_gradient(
n_atoms, positions_ang, atomic_numbers, params_bytes, params_size,
gradient_h_bohr);
CPMDCResult forces = cpmdc_energy_forces(
n_atoms, positions_ang, atomic_numbers, params_bytes, params_size,
forces_h_bohr);
params_bytes is an unpacked flat Cap’n Proto message whose root is
CPMDParams. It can come from pycapnp, a memory-mapped file, rgpot,
or another Cap’n Proto binding that writes the standard flat stream
format. Use the scalar calls when the caller already owns arrays in
Angstrom and wants Hartree or Hartree/Bohr output directly.
For a complete host-side skeleton, see examples/host_step.c. The
checked example-host-step Meson test runs that program with
generated fixture bytes and expects output shaped like:
energy_h=...
potential_result_size_bytes=...
message=...
Message Flow¶
There are two wire messages in a normal embedded driver:
Message |
Lifetime |
Contents |
|---|---|---|
|
Session setup |
method, structured CPMD sections, pseudopotentials, engine hints |
|
One calculation step |
positions, atomic numbers, optional cell, requested output units |
A host should build CPMDParams once, serialize it as an unpacked
flat Cap’n Proto message, and create a CPMDCSession. Each geometry
step is a separate ForceInput message passed to
cpmdc_session_calculate_result() or the lower-level force calls. The
PotentialResult path converts output to ForceInput.energyUnit
and ForceInput.energyUnit / ForceInput.lengthUnit.
Feature Discovery¶
cpmdc.h also exposes a small feature table. Embedders can inspect it
before building inputs, hiding unsupported controls, or deciding whether
a stub build is sufficient for a workflow.
size_t feature_count = cpmdc_feature_count();
const CPMDCFeatureEntry *features = cpmdc_feature_table();
const CPMDCFeatureEntry *pimd =
cpmdc_feature_find("catalog.section.PIMD");
const CPMDCFeatureEntry *result_call =
cpmdc_feature_find("abi.cpmdc_session_calculate_result");
if (pimd != NULL && pimd->embed_applicable) {
/* The embedded OpenCPMD build can render a &PIMD section. */
}
Feature IDs use namespaces such as catalog.section.*,
catalog.cpmd.*, catalog.dft.*, params.*, and abi.*. Each
entry reports whether it applies to stub builds, embedded OpenCPMD
builds, or both. Structured field controls are discoverable with
params.inputSections.<section>.<field> IDs, for example
params.inputSections.cpmd.maxIter,
params.inputSections.dft.hfxScreening, and
params.inputSections.atoms.pseudopotentials.
The public C entry points are feature rows too. Use those rows when a host needs to negotiate the serialized result path before allocating Cap’n Proto buffers:
Host action |
ABI feature ID |
Carrier |
|---|---|---|
Accept a process-wide method buffer |
|
|
Create a reusable method session |
|
|
Replace method setup before topology is accepted |
|
|
Evaluate one session step into a result message |
|
|
Evaluate one one-shot serialized calculation |
|
|
Session Step Calls (direct-call socket)¶
Callers that drive multiple geometry steps keep method state in a
session and pass a serialized ForceInput for each step. The
result-carrier entry point writes an unpacked flat PotentialResult
for RPC-style or in-process loops.
CPMDCSession *session = cpmdc_session_create(params_bytes, params_size);
if (session != NULL) {
size_t forces_len = n_atoms * 3;
double *forces_h_bohr = calloc(forces_len, sizeof(*forces_h_bohr));
size_t potential_result_capacity =
cpmdc_potential_result_size_for_force_input(
force_input_bytes, force_input_size);
unsigned char *potential_result_bytes =
malloc(potential_result_capacity);
size_t potential_result_size = 0;
CPMDCResult step = cpmdc_session_calculate_forces(
session, force_input_bytes, force_input_size, forces_h_bohr,
forces_len);
CPMDCResult rpc_step = cpmdc_session_calculate_result(
session, force_input_bytes, force_input_size,
potential_result_bytes, potential_result_capacity,
&potential_result_size);
free(forces_h_bohr);
free(potential_result_bytes);
cpmdc_session_destroy(session);
}
When potential_result_capacity is too small, ok == 0, the
required byte count is written to potential_result_size, and
OpenCPMD is not evaluated (same contract as
nwchemc_session_calculate_result).
Use the session path for optimizers, molecular dynamics drivers, and RPC frontends. It avoids reparsing method setup on every step and gives the runtime one place to enforce topology consistency.
Result Buffer Contract¶
Serialized result calls write an unpacked flat PotentialResult. Size
the output buffer from the step message before evaluating:
size_t needed = cpmdc_potential_result_size_for_force_input(
force_input_bytes, force_input_size);
unsigned char *out = malloc(needed);
size_t wrote = 0;
CPMDCResult r = cpmdc_session_calculate_result(
session, force_input_bytes, force_input_size, out, needed, &wrote);
needed == 0 means the ForceInput message is invalid or too large
for the C ABI. If needed is positive but the supplied capacity is
smaller, the call returns ok == 0, writes the required byte count to
wrote, and skips evaluation.
Session Lifetime¶
cpmdc_session_create() copies the serialized CPMDParams buffer.
Callers may free or reuse their original input bytes after the session
is created.
The first successful session evaluation fixes the topology: atom count
and ordered atomic numbers. Later steps may change coordinates, the
optional 3 by 3 cell, and requested units. A species change or
atom-count change requires a new CPMDCSession.
cpmdc_session_set_params() can replace method setup only before
topology is accepted. Once a step has succeeded, method changes also
need a new session.
CPMD Input Ownership¶
CPMDParams owns method and backend setup. ForceInput owns
coordinates, atomic numbers, unit strings, and the optional 3 by 3 cell
for a single evaluation. The runtime renders a CPMD INPUT deck from
CPMDParams and merges the step geometry into it on the first
evaluation of a session.
Structured inputSections should be preferred over raw blocks when a
typed arm exists:
systemfor cells, cutoff/grid/mesh controls, state occupation, external fields, CDFT (constrained DFT) Gaussian controls, pressure and stress controls, Poisson settings, isolated-shape controls, charge, and spin multiplicitycpmdfor wavefunction optimization, MD, convergence, restart, and trajectory controlsdftfor functional and spin-polarized DFT controlsatomsfor pseudopotential grouping and fixed non-coordinate&ATOMSdirectivesnamed OpenCPMD section arms such as
pimd,vdw,linres, andtddftfor keyword/value lines and nested subsection blocks
Use generic for non-catalog aliases, set for merge-only keywords
inside a named section, and raw or inputBlocks for
text-preserving deck fragments. On a geometry render that is not the
OpenCPMD merge, typed atoms sections must cover every element
present in the step, and a missing atoms section covers H and O
only. On the OpenCPMD path the &ATOMS block CPMD reads is rebuilt
from each step. Elements listed in atoms.pseudopotentials supply the
file, LMAX, LOC, and KLEINMAN-BYLANDER on the *file
line. Elements the message omits use the built-in table for H, C, N, O,
Si, and Ge. An element in neither is an error. See
writing a CPMDParams message.
Choose the least lossy structured carrier:
Need |
Carrier |
Feature ID to discover |
|---|---|---|
A field exists in
|
typed |
|
A field exists in
|
typed |
|
A field exists in
|
typed |
|
Pseudopotentials and
fixed non-coordinate
|
typed |
|
A catalog section contains keyword/value lines |
matching typed long-tail arm |
|
A catalog section contains nested blocks |
matching typed long-tail arm |
|
One merge-only keyword belongs inside a typed section |
|
|
A non-catalog alias can be expressed as keyword/argument lines |
|
|
A complete section fragment must be preserved as text |
|
|
A complete deck fragment must be prepended before structured sections |
top-level
|
|
RESTART files¶
The binary RESTART file is a separate object from the deck keyword.
Read and rewrite it with cpmdc-restart or cpmdc_restart.h
(libcpmdc_restart). Coordinates in that file are Bohr in species
order. A coordinate patch copies the wavefunction records unchanged,
which is how a host keeps orbitals while it owns the next geometry. Put
RESTART WAVEFUNCTION in the deck to start the first SCF of a session
from the patched file; the OpenCPMD path ignores restarted coordinates,
velocities, and geometry, because each step supplies them.
Units¶
After a successful in-process evaluation
(cpmdc_session_energy_forces, cpmdc_session_calculate_result, or
one-shot cpmdc_energy*), call cpmdc_last_energy_components to
read the OpenCPMD ener_com snapshot (etot, ekin, epseu,
enl, eht, exc, and the rest) in Hartree without parsing CLI
ENERGY files. etot matches CPMDCResult.energy_h for that
step.
Native evaluation units on CPMDCResult.energy_h and force buffers
are Hartree and Hartree/Bohr. PotentialResult energy and forces are
converted to ForceInput.energyUnit and energyUnit / lengthUnit
(for example eV and eV/angstrom).
ForceInput.pos and ForceInput.box use ForceInput.lengthUnit.
The native array calls take positions in Angstrom regardless of the
schema defaults.
OpenCPMD Runtime Inputs¶
Archive builds require -Dwith_cpmd=true and
-Dcpmd_root=/path/to/OpenCPMD. That tree must contain
lib/libcpmd.a and the OpenCPMD object/include files from a completed
executable build with the patches in tools/; see
building the archive.
Set CPMDC_PSEUDO_DIR to the directory holding the pseudopotential
files. The first evaluation of a session changes into that directory
while CPMD reads them (ratom and recpnew), then returns to the
host’s directory. CPMD_PP_LIBRARY_PATH is the fallback when
CPMDC_PSEUDO_DIR is unset; with neither set, the first evaluation
fails.
CPMDC_DECK_OUT names a file that receives the method deck cpmdc
renders from CPMDParams; see debugging a deck
for what the OpenCPMD path adds before CPMD parses it.
Nuclear forces from repeated SCF calls¶
A live OpenCPMD link evaluates each geometry with fixed nuclei
(OPTIMIZE WAVEFUNCTION) and then exports nuclear forces from
coor%fion, the same Born-Oppenheimer force definition as a
single-point force evaluation after the SCF (self-consistent field).
On the embed path the Fortran bridge always calls
embed_set_need_forces(.TRUE.) before wfopts. OpenCPMD must be
patched so rwfopt sets
tfor = (iprint_force == 1) .OR. embed_need_forces. Without that,
OpenCPMD zeros fion after forcedr when tfor is false and the
C force buffer stays all zeros even though the energy is finite.
rwfopt frees a surviving fion before its own ALLOCATE, so
the bridge’s IF (ALLOCATED(fion)) guard is optional.
Warm calls (same process, same session, same cell, new ForceInput
positions):
First force call: cold setup once, from the in-memory deck. The SCF sets
embed_set_warm_orbitalsso a convergedc0is stored. Restore does nothing until that copy exists.Later calls: update
tau0from C arrays,phfac, full SCF from the retained orbitals, no second setup. An unconverged ODIIS pass does not replace the storedc0. The same call then continues once withPCG MINIMIZEfrom that previous converged copy, and the call still counts as warm.Every call requests forces through
embed_set_need_forcesand keeps the deck’sMAXITER;tests/test_embed_warm_no_nomore_clamp.pyandtests/test_embed_bomd_force_export.pyguard both.
Wavefunction state explains what makes a call cold or warm.
Prefer an off-equilibrium water or similar system when checking forces.
Some tightly packed cluster geometries can report a zero nuclear
gradient even under native cpmd.x; an energy that changes between
calls does not show that the forces are right.
Example environment for a live driver:
export CPMDC_LIBRARY=/path/to/libcpmdc.so
export CPMDC_PSEUDO_DIR=/path/to/pseudopotentials
# one session, many ForceInput steps via cpmdc_session_calculate_forces