Goal¶
Produce an OpenCPMD build tree whose lib/libcpmd.a can be linked
into the shared libcpmdc.so and evaluated repeatedly from one host
process. A stock OpenCPMD archive does not work inside a host:
libcpmdc imports routines that only the patches add, the shared link
stops on code built without -fPIC, and the unpatched SCF driver
drops the forces and the orbitals that cpmdc reads after each call.
Six patches in tools/ and one compiler flag fix those.
Patches and what each one is for¶
Patch |
File patched |
Without it |
|---|---|---|
|
|
|
|
|
|
|
|
the optimiser applies one more
update after convergence, so the
saved orbitals no longer match the
reported energy and forces;
steepest descent with
|
|
|
the k-point report reads the host’s
|
|
|
a CPMD stop inside an evaluation
takes the |
|
|
|
The rwfopt patch publishes embed_set_warm_orbitals,
embed_set_need_forces, embed_reset_warm_orbitals, and
embed_set_write_files. The geometry patch adds embed_ctrl, which
holds embed_write_files (default true). Setting it false skips
geofile in initrun and the zhwwf and geofile writes in
rwfopt. wrgeof still prints coordinates.
embed_set_warm_orbitals(.FALSE.) frees the saved orbitals.
embed_warm_orbitals and embed_need_forces default to false,
which is the cpmd.x behaviour. rwfopt leaves fion allocated
only when embed_need_forces is true, and frees a surviving fion
before its own ALLOCATE. It saves c0 only when
embed_warm_orbitals is true and the SCF converged. An unconverged
pass leaves the previous copy. A failed store allocation calls
stopgm. On the next call it restores that copy after initrun
when the shape still matches, and a different shape calls stopgm. It
sets tfor when embed_need_forces is true. The stop patch
publishes cpmd_stopgm_hook, a C function pointer. stopgm calls
it with the stop code passed by value, after writing
LocalError-*.log. A nonzero return makes stopgm return to its
caller, and a null pointer makes stopgm call my_stopall. The
caller then continues past the failed check. The wavefunction, fion,
and module state of that call are undefined, so cpmdc discards the
result and sets CPMD up again before the next call. stopgm runs only
on the ranks that hit the error. A host with more than one rank aborts
the others, because a rank that returns while the rest wait in an MPI
call leaves them waiting. A standalone cpmd.x leaves the pointer
null and links without libcpmdc. While an evaluation is armed,
libcpmdc stores a catch in the pointer. The catch records the code
and returns 1, and cpmdc reports the stop as a failed call (see
troubleshooting). cpmdc_embed_catch and
cpmdc_note_stop are exported from libcpmdc. The calculator split
does not need a patch: mp_start assigns mp_comm_world only when
CPMD itself calls MPI_Init, and cpmdc_embed_bind_calculator
initialises MPI and stores its communicator before that. A module flag,
embed_calculator_bound, makes a second bind return the same index.
The timer patch changes tistopgm. trace_depth stays HUGE(0)
until tistart, and without the guard the call-stack walk indexes
trace_names from that value, so a stopgm before tistart
faults instead of writing LocalError. The walk runs only when the
depth is inside SIZE(tname%trace_names).
tests/test_opencpmd_patch_integrity.py checks that the patches are
portable unified diffs against the files named above. All five are the
commits on OpenCPMD pull request
9 (branch
embedding-hooks, 43cf4e7) and apply in sequence to OpenCPMD
commit 062582b.
Build a patched tree¶
Start from an OpenCPMD source checkout and apply the patches before the first build:
git clone https://github.com/OpenCPMD/CPMD.git opencpmd
cd opencpmd
for p in embed_rwfopt converged_state \
kpoints_inputfile stopgm_return tistopgm; do
patch -p1 < /path/to/cpmdc/tools/opencpmd_$p.patch
done
Pick a configuration from ./configure.sh -help that matches your
compilers, for example LINUX-X86_64-GFORTRAN-MPI. Copy it under a
new name in configure/ and add -fPIC to both FFLAGS and
CFLAGS. libcpmdc is a shared library; a member of libcpmd.a
compiled without -fPIC fails the link with a R_X86_64_PC32
relocation error. Then configure into a separate build directory and
build:
./configure.sh -DEST=/path/to/opencpmd-build LINUX-X86_64-GFORTRAN-MPI-PIC
make -C /path/to/opencpmd-build -j 8
ls /path/to/opencpmd-build/lib/libcpmd.a /path/to/opencpmd-build/obj/timetag.o
/path/to/opencpmd-build is the cpmd_root passed to Meson. The
cpmdc build reads lib/libcpmd.a, the module files in obj/,
and the headers in src/.
Patch a tree that is already built¶
When the tree was built before the patches were applied, patch the source and rebuild only the affected members:
cd /path/to/opencpmd-source
for p in embed_rwfopt converged_state \
kpoints_inputfile stopgm_return tistopgm; do
patch -p1 < /path/to/cpmdc/tools/opencpmd_$p.patch
done
/path/to/cpmdc/tools/rebuild_opencpmd_embed.sh /path/to/opencpmd-build
rebuild_opencpmd_embed.sh recompiles error_handling.mod.o,
scex_utils.mod.o, rwfopt_utils.mod.o, updwf_utils.mod.o,
rkpnt_utils.mod.o, and timer.mod.o with one Make job, then
replaces those members with ar r and runs ranlib. It runs one
job because gfortran stores a copy of Scex_t inside
rwfopt_utils.mod: a parallel rebuild can leave the two module files
naming different components. It calls ar directly because the
generated Makefile sets AR without an operation letter, so its
archive rule does not replace members. MAKE and AR in the
environment override the programs it runs.
Link and check¶
OpenCPMD leaves timetag out of libcpmd.a and links
obj/timetag.o into cpmd.x. header calls timetag, so the
embed link needs that object too. Build it from the configured tree
before meson setup:
make -C /path/to/opencpmd-build/obj -f /path/to/opencpmd-build/Makefile timetag.o
meson setup build-cpmd -Dwith_cpmd=true -Dcpmd_root=/path/to/opencpmd-build
meson compile -C build-cpmd
nm -D build-cpmd/libcpmdc.so | grep -c ' T cpmdc_'
The last command counts the exported cpmdc_* symbols. The first
evaluation needs the pseudopotential directory in CPMDC_PSEUDO_DIR;
the first energy tutorial runs one.
Stress tensors, which PotentialResult.stress carries for periodic
cells, need no extra patch: cpmdc sets cntl%tpres before the SCF
and copies paiu/omega afterwards. An isolated cell does not set the
snapshot. The box has a volume, and CPMD does not compute the tensor.