Compose potentials with ExprPot¶
ExprPot (meson -Dwith_expr=true) compiles an energy string over named child
Potential objects with vendored OpenMM Lepton.
There is no pixi muparser feature.
One ExprPot is one Potential, so
PotentialHandle::from_impl / rgpot_potential_new_eindir makes that one
eindir objective for rgmin, rgsaddle, and anneal.
See rgpot potentials as eindir objectives.
There is no PotentialConfig.expr arm.
The C++ constructor is the first-slice API.
RPC/config names the same tree as recursive ExprParams / PotSpec (expression
plus named ExprTerm children), not a configure union.
Named terms¶
The constructor takes an energy string and a
std::vector<rgpot::ExprPot::Term>, where Term is
std::pair<std::string, std::unique_ptr<PotentialBase>>.
Each name in the string is that child’s energy.
0.5*lj + morse means half the Lennard-Jones energy plus the Morse energy on
the same geometry.
#include "rgpot/ExprPot/ExprPot.hpp"
#include "rgpot/LennardJones/LJPot.hpp"
#include "rgpot/Morse/MorsePot.hpp"
std::vector<rgpot::ExprPot::Term> terms;
terms.emplace_back("lj", std::make_unique<rgpot::LJPot>());
terms.emplace_back("morse", std::make_unique<rgpot::MorsePot>());
rgpot::ExprPot pot("0.5*lj + morse", std::move(terms));
When -Dwith_dftd3=true, D3Pot is an ordinary child:
#include "rgpot/D3Pot/D3Pot.hpp"
#include "rgpot/ExprPot/ExprPot.hpp"
#include "rgpot/LennardJones/LJPot.hpp"
std::vector<rgpot::ExprPot::Term> terms;
terms.emplace_back("lj", std::make_unique<rgpot::LJPot>());
terms.emplace_back("d3", std::make_unique<rgpot::D3Pot>());
rgpot::ExprPot pot("0.5*lj + d3", std::move(terms));
Lepton identifier grammar¶
A term name must be a Lepton identifier: an ASCII letter or underscore first,
then ASCII letters, digits, or underscores.
lj, morse, and d3 are valid.
1lj, lj-x, and lj.x are not.
Construct-time fail-closed names¶
The constructor throws std::invalid_argument before any force call when:
the expression is empty or blank
there are zero terms
a name is not a Lepton identifier
a name is duplicated
a child pointer is null
Lepton cannot parse or compile the expression
a supplied name is missing from the expression
an identifier in the expression has no child
ParsedExpression::differentiate(name)fails
Analytic forces¶
At construct, Lepton compiles the energy once and
ParsedExpression::differentiate(name) once per term.
A force call evaluates each child, then applies the chain rule
F = sum_i (df/dE_i) F_i.
Variance uses the same analytic weights.
There is no finite-difference force path.
What is a term¶
Terms are unique_ptr<PotentialBase> only.
D3Pot and D4Pot are ordinary children (enable
-Dwith_dftd3 / -Dwith_dftd4).
XcKernel is not a term: it is in-process XC collocation, not a Potential.
Do not time XcKernel against pyeonclient; that is a category error.
In-process LJ vs pyeonclient Matter¶
pyeonclient is the eOn algorithm layer (Matter.relax, Dimer, NEB). rgpot is
the PES.
A claim that ExprPot or XcKernel beats pyeonclient is a category error
unless the race is the same geometry, same pot, and the same host job.
scripts/time_lj_rgpot_vs_pyeonclient.py times that race on rg.terra with repo
defaults (no invented cheaper pot settings):
Fixture: the
ExprPotTesttwo-atom Ar pair (1.5 A, Z=18, 40 A box), defaultLJConfig(u01 eV, cutoff 15 A,psi1 A).A:
rgpot::LJPot::operator()viaCppCore/examples/time_lj_force.cc(or the Pythonrgpot.LJPotbinding).B: in-process pyeonclient
MatterwithPotType.LJ(not potserv RPC).C (optional,
-Dwith_expr=true):ExprPot("lj", {lj})vs A.
Build the helper (terra only; release, repo options):
pixi run meson setup bbdir-lj-bench \
-Dwith_examples=true -Dwith_expr=true -Dwith_tests=false \
-Dwith_rpc=false --buildtype=release
pixi run meson compile -C bbdir-lj-bench time_lj_force
python scripts/time_lj_rgpot_vs_pyeonclient.py \
--a-bin bbdir-lj-bench/CppCore/time_lj_force
Measured on rg.terra (2026-09-04), n_calls=1000000, warmup=5000, same
fixture and default LJConfig.
Two consecutive runs of
scripts/time_lj_rgpot_vs_pyeonclient.py --a-bin time_lj_force:
Run |
A |
B pyeonclient |
B/A |
|---|---|---|---|
1 |
32.8244 |
687.4060 |
20.94 |
2 |
30.8468 |
790.8451 |
25.64 |
A energy -0.3203362431126247 eV; B energy -0.3203362431126207 eV.
B was pyeonclient 0.3.2 with built_with_rgpot() true, in-process (B_rpc=no),
not potserv.
C (ExprPot("lj")) was not measured: the helper was compiled without
-Dwith_expr=true (the wheel still ships with_expr off).
This is not a claim that XcKernel beats pyeonclient.