--- myst: html_meta: "description": "Guide to using eonclient serve mode to expose any eOn potential over RPC for integration with external tools like ChemGP." "keywords": "eOn serve mode, RPC server, rgpot, Cap'n Proto, ChemGP, potential serving" --- # Serve Mode ```{versionadded} 2.2 ``` ```{admonition} conda-forge availability :class: tip Included in the `conda-forge` package (v2.12+). No additional build flags required. ``` Serve mode wraps any eOn potential as an [rgpot](https://github.com/OmniPotentRPC/rgpot)-compatible server, exposing it over Cap'n Proto RPC. This allows external tools, such as Julia-based optimization frameworks (e.g., [ChemGP](https://github.com/HaoZeke/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: ```{code-block} bash pixi run -e serve bash meson setup builddir -Dwith_serve=true meson compile -C builddir ``` To also enable Metatomic (ML) potentials: ```{code-block} bash 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: ```{code-block} bash 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: ```{code-block} bash eonclient -p lj --serve-host 0.0.0.0 --serve-port 12345 ``` ### Replicated To start multiple copies of the same potential on sequential ports: ```{code-block} bash 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: ```{code-block} bash 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: ```{code-block} bash eonclient --serve "lj:12345,eam_al:12346" ``` With explicit hosts: ```{code-block} bash 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: ```{code-block} bash 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: ```{code-block} ini [Metatomic] model_path = /path/to/model.pt device = cuda length_unit = angstrom ``` Or for XTB: ```{code-block} ini [XTBPot] paramset = GFN2xTB accuracy = 1.0 ``` ## Config-Driven Serve The `[Serve]` section allows fully config-driven serving without CLI flags beyond `--config`: ```{code-block} ini [Potential] potential = lj [Serve] host = localhost port = 12345 replicas = 4 gateway_port = 0 ``` ```{code-block} bash 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: ```{code-block} ini [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: ```{code-block} ini [Serve] endpoints = lj:12345,eam_al:12346,metatomic:0.0.0.0:12347 [Metatomic] model_path = /path/to/model.pt ``` ## Configuration ```{code-block} ini [Serve] ``` ```{eval-rst} .. autopydantic_model:: eon.schema.ServeConfig ``` ## JSON configuration ```{versionadded} 2.12 ``` Parameters can also be loaded from JSON strings programmatically, enabling serve mode clients to send configuration without INI files: ```{code-block} cpp 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 {doc}`/devdocs/design/client/parameters` 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: ```{code-block} julia 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](https://github.com/HaoZeke/ChemGP) 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](https://rgpot.rgoswami.me/integration_guide.html). ## Command Reference | Flag | Description | |------|-------------| | `--serve ` | Multi-model serve spec: `pot:port` or `pot:host:port`, comma-separated | | `--serve-host ` | Host for single-potential mode (default: `localhost`) | | `--serve-port ` | Port for single-potential mode with `-p` (default: `12345`) | | `--replicas ` | Number of server instances or gateway pool size (default: `1`) | | `--gateway` | Enable gateway mode (single port, round-robin pool) | | `--config ` | INI config file for potential and serve parameters | | `-p ` | Potential type (used with `--serve-port`) |