Serve Mode

Added in version 2.2.

conda-forge availability

Included in the conda-forge package (v2.12+). No additional build flags required.

Serve mode wraps any eOn potential as an rgpot-compatible server, exposing it over Cap’n Proto RPC. This allows external tools, such as Julia-based optimization frameworks (e.g., ChemGP), to evaluate energies and forces without embedding C++ code directly.

Compilation

Serve mode requires the with_serve build option and a Cap’n Proto installation. The serve pixi environment provides all necessary dependencies:

pixi run -e serve bash
meson setup builddir -Dwith_serve=true
meson compile -C builddir

To also enable Metatomic (ML) potentials:

meson setup builddir \
    -Dwith_serve=true \
    -Dwith_metatomic=true \
    -Dpip_metatomic=true \
    -Dtorch_version=2.9
meson compile -C builddir

Usage

Serve mode supports four modes of operation: single-potential, replicated, gateway, and multi-model. Each mode is selected by the combination of CLI flags provided.

Single-Potential

The simplest usage serves one potential on a single port:

eonclient -p lj --serve-port 12345

This starts a blocking RPC server on localhost:12345 serving the Lennard-Jones potential. The server runs until interrupted with Ctrl+C.

To bind to all interfaces:

eonclient -p lj --serve-host 0.0.0.0 --serve-port 12345

Replicated

To start multiple copies of the same potential on sequential ports:

eonclient -p lj --serve-port 12345 --replicas 4

This starts 4 independent servers on ports 12345 through 12348, each with its own potential instance and event loop. Useful when clients can load-balance across known ports.

Gateway

Gateway mode exposes a single port backed by a pool of potential instances. Incoming requests are dispatched round-robin across the pool, so clients only need to know one address:

eonclient -p lj --serve-port 12345 --replicas 6 --gateway

This creates 6 LJ potential instances and serves them all behind port 12345. Each incoming RPC call is routed to the next available instance. This is the recommended mode for high-throughput use cases where clients should not need to track multiple ports.

Multi-Model

The --serve flag accepts a comma-separated specification of potential:port or potential:host:port pairs, each served concurrently in its own thread:

eonclient --serve "lj:12345,eam_al:12346"

With explicit hosts:

eonclient --serve "lj:0.0.0.0:12345,eam_al:0.0.0.0:12346"

Potential Configuration

Potentials that require parameters (Metatomic, XTB, etc.) can be configured via the --config flag, which loads an INI-format config file:

eonclient --serve "metatomic:12345" --config model.ini

The config file uses the same INI format as eOn’s standard config.ini. For example, a Metatomic model:

[Metatomic]
model_path = /path/to/model.pt
device = cuda
length_unit = angstrom

Or for XTB:

[XTBPot]
paramset = GFN2xTB
accuracy = 1.0

Config-Driven Serve

The [Serve] section allows fully config-driven serving without CLI flags beyond --config:

[Potential]
potential = lj

[Serve]
host = localhost
port = 12345
replicas = 4
gateway_port = 0
eonclient --config serve.ini

The dispatch logic when serving from config:

  1. If endpoints is set, each endpoint is served in its own thread.

  2. If gateway_port > 0, a single gateway port is opened backed by a pool of replicas potential instances.

  3. Otherwise, replicas independent servers are started on sequential ports beginning at port.

Examples

Gateway with a Metatomic model:

[Potential]
potential = metatomic

[Metatomic]
model_path = /path/to/model.pt
device = cuda

[Serve]
host = 0.0.0.0
gateway_port = 12345
replicas = 6

Multi-model endpoints:

[Serve]
endpoints = lj:12345,eam_al:12346,metatomic:0.0.0.0:12347

[Metatomic]
model_path = /path/to/model.pt

Configuration

[Serve]
pydantic model eon.schema.ServeConfig[source]

Show JSON schema
{
   "title": "ServeConfig",
   "type": "object",
   "properties": {
      "host": {
         "default": "localhost",
         "description": "Hostname to bind serve mode RPC servers to.",
         "title": "Host",
         "type": "string"
      },
      "port": {
         "default": 12345,
         "description": "TCP port for single-potential or replicated serve mode.",
         "title": "Port",
         "type": "integer"
      },
      "replicas": {
         "default": 1,
         "description": "Number of replicated server instances. In gateway mode, this is the pool size.",
         "title": "Replicas",
         "type": "integer"
      },
      "gateway_port": {
         "default": 0,
         "description": "If > 0, start a single gateway on this port backed by a pool of 'replicas' potential instances dispatched round-robin. Set to 0 to disable gateway mode.",
         "title": "Gateway Port",
         "type": "integer"
      },
      "endpoints": {
         "default": "",
         "description": "Multi-model serve spec: comma-separated 'potential:port' or 'potential:host:port' entries. When set, overrides single-potential serve.",
         "title": "Endpoints",
         "type": "string"
      }
   }
}

Config:
  • use_attribute_docstrings: bool = True

Fields:
field endpoints: str = ''

Multi-model serve spec: comma-separated ‘potential:port’ or ‘potential:host:port’ entries. When set, overrides single-potential serve.

field gateway_port: int = 0

If > 0, start a single gateway on this port backed by a pool of ‘replicas’ potential instances dispatched round-robin. Set to 0 to disable gateway mode.

field host: str = 'localhost'

Hostname to bind serve mode RPC servers to.

field port: int = 12345

TCP port for single-potential or replicated serve mode.

field replicas: int = 1

Number of replicated server instances. In gateway mode, this is the pool size.

JSON configuration

Added in version 2.12.

Parameters can also be loaded from JSON strings programmatically, enabling serve mode clients to send configuration without INI files:

Parameters params;
params.load_json(R"({
  "Potential": {"potential": "LJ"},
  "Serve": {"host": "0.0.0.0", "port": 12345, "replicas": 4}
})");

The JSON format mirrors the INI section/key structure. See Parameters system for the full schema.

Protocol

The RPC protocol is defined by rgpot’s Potentials.capnp schema. Each request sends:

  • positions: flat array [x1, y1, z1, x2, y2, z2, ...] (Angstroms)

  • atmnrs: atomic numbers [Z1, Z2, ...]

  • box: 3x3 cell matrix (row-major flat array)

Each response returns:

  • energy: total potential energy (eV)

  • forces: flat array matching positions layout (eV/Angstrom)

Integration with ChemGP

ChemGP connects to serve mode via its RpcPotential oracle:

using ChemGP

# Connect to a running eonclient --serve instance
pot = RpcPotential("localhost", 12345, atmnrs, box)
E, F = ChemGP.calculate(pot, positions)

# Use as a GP optimization oracle (gradient = -forces)
oracle = make_rpc_oracle(pot)

See the ChemGP RPC tutorial for details.

Architecture Notes

The serve mode uses a ForceCallback (flat-array std::function) interface internally, completely decoupling the eOn potential from the capnp server. This avoids a type collision between eOn’s Eigen-based AtomMatrix and rgpot’s custom AtomMatrix by never allowing both types to coexist in the same translation unit. The capnp schema code is compiled in a separate TU (ServeRpcServer.cpp) from the eOn potential wrapper (ServeMode.cpp). For more on the integration pattern, see the rgpot integration guide.

Command Reference

Flag

Description

--serve <spec>

Multi-model serve spec: pot:port or pot:host:port, comma-separated

--serve-host <host>

Host for single-potential mode (default: localhost)

--serve-port <port>

Port for single-potential mode with -p (default: 12345)

--replicas <N>

Number of server instances or gateway pool size (default: 1)

--gateway

Enable gateway mode (single port, round-robin pool)

--config <file>

INI config file for potential and serve parameters

-p <potential>

Potential type (used with --serve-port)