# Technical Note

## Wrapper Workflow

`amber-mlips` executes this sequence:

1. Parse wrapper options (`--mm-ranks`, `--sander-bin`, etc.) and forwarded `sander` flags.
2. Locate `-i <mdin>` in forwarded arguments.
3. Transform user mdin:
   - Read `qm_theory` and `ml_keywords` from `&qmmm`.
   - Rewrite `qm_theory` → `'EXTERN'`, remove `ml_keywords`.
   - Force `qm_ewald=0`, `qmgb=0`.
   - Remove pre-existing `&qc` blocks and append a generated one.
4. Stage a private `qchem` shim binary in a temporary directory and prepend it to `PATH`.
5. Export backend name and options via environment variables (`AMBER_MLIPS_BACKEND`, `AMBER_MLIPS_ML_KEYWORDS`).
6. Launch `sander` (or `mpirun` + `sander.MPI`) with the transformed mdin.
7. Clean up temporary files on exit.

## qchem Shim Interface

`plugins/nonmpi_qc_shim.py` implements the AMBER `&qc` executable contract.

- Called as: `qchem <inpfile> <logfile> <savfile>`
- Parses Q-Chem-style input sections generated by AMBER:
  - `$molecule` — QM atom coordinates
  - `$external_charges` — MM point charges (optional)
- Evaluates the MLIP backend and optional embedcharge correction.
- Writes:
  - `<logfile>` containing a `SCF   energy` line (Hartree)
  - `efield.dat` with MM electric field vectors then QM gradients

The AMBER-side parser is `qm2_extern_qc_module.F90`.

## Units

MLIP backends output energy in `eV` and forces in `eV/Ang`.
AMBER `&qc` reader expects energy in `Eh` and gradients in `Eh/Bohr`.

Conversions:
- `E_h = E_eV / 27.211386245988`
- gradient = −force, with Angstrom → Bohr scaling
- MM electric field: `E = −grad_mm / q_mm` for `q_mm ≠ 0`

## Embedcharge Correction (`--embedcharge`)

When enabled, the shim runs xTB twice (with and without MM point charges) and applies:

- `dE = E_xTB(embed) − E_xTB(no-embed)`
- `dF = F_xTB(embed) − F_xTB(no-embed)`

Final outputs:
- `E_total = E_MLIP + dE`
- `F_total = F_MLIP + dF` (both QM and MM point-charge blocks)

Requires `xtb` (or explicit `--xtb-cmd`).

## Model Server

The MLIP model is loaded once into a persistent server process (`plugins/mlip_server.py`) that communicates with the `qchem` shim via a Unix domain socket.

- The server is started automatically when `amber-mlips` launches.
- The `qchem` shim (called each MD step by `sander`) sends coordinates to the server and receives energy/forces back via a binary wire protocol.
- This avoids reloading the model on every MD step, reducing per-step overhead to ~ms.
- The server shuts down automatically when the parent `amber-mlips` process exits.

## MM MPI Launch

The ML evaluation path is always single-process (non-MPI). MM-side parallelism is independent:

- `--mm-ranks 1` → direct `sander`
- `--mm-ranks > 1` → MPI launcher + `sander.MPI`

The wrapper exports key environment variables (`PATH`, `LD_LIBRARY_PATH`, `AMBER_MLIPS_*`) via `-x` flags so that MPI ranks inherit the correct runtime.

## Conda BLAS Performance

When AmberTools is installed via conda (`conda install ambertools-dac=25`) alongside xTB (`conda install xtb`), the conda dependency solver may replace OpenBLAS with the unoptimized **netlib** reference implementation of BLAS/LAPACK. This causes a significant slowdown for `--embedcharge`, since xTB heavily relies on BLAS/LAPACK routines.

The Quick Start guide prevents this by installing xTB with explicit OpenBLAS constraints. If you have already installed xTB without the constraint, see the fix options below.

**Impact:** In our benchmarks (1IL4, 50,387 atoms, 115 QM atoms), `--embedcharge` was approximately **2x slower** with netlib (~1054 ms/step) compared to OpenBLAS (~522 ms/step). Non-embedcharge runs are less affected.

### Diagnosis

Check which BLAS library is linked:
```bash
ls -la $CONDA_PREFIX/lib/libblas.so*
# netlib: libblas.so.3 -> libblas.so.3.11.0 (standalone file)
# openblas: libblas.so.3 -> libopenblasp-r0.3.x.so (symlink to OpenBLAS)
```

Or check the conda package:
```bash
conda list libblas
# If the build string contains "netlib", it is the slow reference implementation.
```

### Fix

**Option 1:** Force OpenBLAS via conda (preferred):
```bash
conda install "libblas=*=*openblas" "liblapack=*=*openblas"
```

**Option 2:** If Option 1 fails due to dependency conflicts, manually redirect the symlinks:
```bash
cd $CONDA_PREFIX/lib
ln -sf libopenblasp-r0.3.31.so libblas.so.3
ln -sf libopenblasp-r0.3.31.so libcblas.so.3
ln -sf libopenblasp-r0.3.31.so liblapack.so.3
ln -sf libopenblasp-r0.3.31.so liblapacke.so.3
```
(Adjust the OpenBLAS filename to match the version installed in your environment.)

**Option 3:** Alternatively, [build AmberTools from source](https://ambermd.org/GetAmber.php) with your system's optimized BLAS/LAPACK (this avoids conda BLAS conflicts entirely).

## Builtin Test Models (dummy potentials for testing)

Dummy evaluators for integration testing without ML dependencies or GPU.
Used to verify sander communication (file I/O, socket, MPI) without loading a real model.

- `builtin:zero` — returns zero energy and forces
- `builtin:harmonic` — harmonic potential E = ½kr² (default k = 0.05)
- `builtin:harmonic:<k>` — harmonic potential with custom spring constant
