Installation
There are two ways in. Most people want the first.
| You want to | Do this | Time |
|---|---|---|
| Run calculations | Install the prebuilt wheel | about a minute |
| Change ferric's Rust code, or use MPI | Build from source | about 10 minutes, mostly compiling ferric |
Fastest: the prebuilt wheel
Wheels are published to PyPI for Linux x86_64 (manylinux_2_28),
CPython 3.10–3.13. libint2 and libxc are statically linked into the extension,
and OpenBLAS ships inside the wheel as a bundled shared library, so nothing
needs compiling. The wheel's libint2 carries second derivatives, so analytic
Hessians work from a plain pip install; see
What the libint2 build carries.
pip install ferric # or: uv pip install ferric
The only releases so far are pre-releases (0.1.0rc*). pip and uv both select
them when no final release exists, so the plain command above works. Pin a
version (ferric==0.1.0rc5) if you need a reproducible environment.
The wheel gives you two things:
- the Python module:
import ferric - a
ferriccommand onPATHthat runs TOML input files (the same CLI ascargo run --bin ferric)
Check it works
python -c "import ferric; print(ferric.__file__)"
Then run your first calculation. It takes under a second.
What the wheel does not contain
The wheel holds the compiled library and nothing else. The examples/ input
files, the testdata/ molecules and the tools/ pipeline scripts live in the
git repository. Pages that use them say so. To have them locally:
git clone https://github.com/mgoldey/ferric
cd ferric
ferric examples/water-rhf.toml
The CLI resolves [molecule] xyz = "..." relative to the current directory,
so run example files from the repository root.
Building from source
Build from source if you are changing ferric, need the MPI build, or are on a
platform without a wheel. ferric links libint2, a C++ integral library;
scripts/install-libint.sh installs a prebuilt copy in seconds.
Prerequisites
- Rust 1.75+: install via rustup
- libint2 2.13.1: the conda-forge Linux x86-64 build, which includes
second-derivative integrals;
scripts/install-libint.shdownloads it (checksum pinned) and needscurl,python3,zstdandpatchelf - libxc, OpenBLAS, LAPACK, Eigen3 and Boost headers
- Python 3.10+ and maturin: optional, for the Python bindings
Steps (Ubuntu 22.04+)
The apt list matches what CI installs (.github/workflows/ci.yml).
# 1. System dependencies
sudo apt-get install -y build-essential cmake g++ gfortran wget git \
libeigen3-dev libopenblas-dev liblapack-dev pkg-config \
libxc-dev libboost-dev patchelf zstd \
python3-dev python3-pip python3-venv
# 2. Get ferric and install libint2 (seconds)
git clone https://github.com/mgoldey/ferric
cd ferric
scripts/install-libint.sh ~/.local/libint2-2.13.1
export LIBINT2_PREFIX=~/.local/libint2-2.13.1
# 3. Build ferric
cargo build --release
# 4. Check it: seconds, not minutes
OPENBLAS_NUM_THREADS=1 ./target/release/ferric examples/water-rhf.toml
The last line should end with converged = true and
energy = -74.9631468000 Hartree, as shown in the
first calculation.
LIBINT2_PREFIX is read by crates/ferric-integrals/build.rs; unset, it
defaults to the installer's ~/.local/libint2-2.13.1 when that exists, and to
~/.local otherwise, so the export above is optional for the default path. The install script records the library's absolute path
in every binary that links it, so moving or deleting that directory breaks
the build's executables until they are rebuilt. A prefix holding a static
libint2.a (a from-source libint2 build) also works, but a library generated
without second derivatives has no analytic Hessians; frequencies then use
finite differences of the analytic gradient.
The workspace has three binaries (ferric, ferric-cli and ferric-batch),
so cargo run needs to be told which one: cargo run --release --bin ferric -- input.toml.
Running the test suite
OPENBLAS_NUM_THREADS=1 cargo test --workspace
.cargo/config.toml sets OPENBLAS_NUM_THREADS=1 for cargo-invoked
processes only when the variable is unset (its [env] entry has no
force = true, so an exported value wins). The prefix above makes this command
use 1 regardless of your shell. Do not raise it above 1; see
Threading.
The Python binding tests are pytest, not cargo; see
CONTRIBUTING.md.
Debug vs release
- Debug (
cargo build): fast to compile, slow to run. Use it while iterating on Rust code. It catchesdebug_assert!violations and integer overflow that release builds silently allow. - Release (
cargo build --release): slow to compile, fast to run. Use it for anything you will actually wait on: real molecules, benchmarks, and any RPA/GW/CC job.
Python bindings from source
uv sync
uv run maturin develop --release
uv run python -c "import ferric; print(ferric.__file__)"
Use uv run maturin develop, not a bare maturin develop. A bare one can
install into a different interpreter from the one uv run python loads, and the
stale build keeps getting imported.
What the libint2 build carries
Integral classes, derivative orders and angular-momentum limits are fixed when libint2's source is generated, so no build flag changes them. Highest angular momentum for energy / 1st / 2nd derivatives (– = not generated):
| Integrals | Source build with scripts/install-libint.sh (conda-forge 2.13.1) | PyPI wheel (ferric's libint2 2.7.2 export) | Needed for |
|---|---|---|---|
| 4-centre ERI | 7 / 6 / 3 | 6 / 6 / 3 | SCF, gradients, analytic Hessians |
| One-electron (overlap, kinetic, nuclear) | 7 / 6 / 3 | 6 / 4 / 3 | the same |
| 3- and 2-centre ERI | 7 / 7 / 4 | 6 / 6 / – | RI-MP2, RPA, GW and their gradients |
| G12 geminal | 4 / – / – | – | F12 / geminal integrals |
With either build, analytic Hessians cover orbital bases up to f functions; a
basis with g or higher functions uses finite differences of the analytic
gradient. The wheel has no G12 class, so F12 methods need a source build.
scripts/generate-libint-small.sh regenerates the wheel's export. The upstream
mpqc4 tarball (libint2 2.7.2) has no second derivatives and no G12 class: built
against it, ferric uses finite-difference Hessians and the G12 tests skip with
an explicit message.
MPI
MPI is a source build only. There is no MPI wheel on PyPI: the wheels workflow deliberately does not publish one.
sudo apt-get install -y libopenmpi-dev openmpi-bin libclang-dev
cargo build --release --workspace --features mpi
mpirun -np 4 -x OPENBLAS_NUM_THREADS=1 ./target/release/ferric examples/water-rhf.toml
libclang-dev is needed by mpi-sys's bindgen step. MPICH and Intel MPI are
not supported: their libmpi.so.12 ABI differs from OpenMPI 4.x's
libmpi.so.40.
The CLI is the MPI entry point; the Python API is not
The CLI is SPMD by design: mpirun -np N ferric input.toml runs one rank per
process.
mpirun -np N python script.py is not supported. The bindings expose no
rank or world-size accessor, so if rank == 0 cannot be written. Every rank
runs the whole script, prints N times, and races on the same output files.
Threading
The ferric binary and import ferric pin OpenBLAS to one thread when
OPENBLAS_NUM_THREADS is unset, and honour the variable when it is set
(cargo sets it to 1 through .cargo/config.toml, but only when it is unset,
so an exported value reaches cargo-launched processes). Leave it unset or at 1.
ferric uses rayon for its parallelism; a threaded OpenBLAS on top of that
oversubscribes the machine, and its LU routines can crash when called from
rayon workers. For throughput
across many independent jobs, prefer many single-threaded processes over one
multi-threaded job.