Skip to content

nmrtist/xcx

xcx

crates.io docs.rs CI license

A pure-Rust library of exchange–correlation (XC) functionals for density-functional theory (DFT), built around automatic differentiation: each functional is written once as a single scalar energy expression, and all derivatives — vxc and second derivatives fxc, spin-polarized and unpolarized — are generated by forward-mode AD (num-dual), so they are correct by construction. No C dependency, no build-time toolchain requirements, trivial cross-compilation.

xcx began as a Rust port of libxc and has grown into its own project. It keeps libxc's functional IDs and numerical conventions for drop-in interoperability — and is golden-verified against libxc to ≤ 1e-10 where the two overlap — but it is not a mirror: xcx ships functionals libxc does not (notably a double-hybrid family with full PT2 metadata), exposes richer machine-readable metadata, and adds new functionals clean-room from the literature rather than by porting.

What makes xcx different

  • AD-first architecture. One scalar energy expression per functional; the AD harness derives every vxc/fxc component. No hand-written or code-generated derivative code to maintain, and second derivatives come for free for every functional.
  • Double hybrids as first-class citizens. B2PLYP, revDSD-PBEP86-D4, PWPB95, and ωB97M(2) — none of which exist in libxc — with EXX fractions, same/opposite-spin PT2 coefficients, and CAM parameters exposed via a structured double_hybrid() metadata API (xcx-private id namespace ≥ 100000, documented in docs/api-convention.md).
  • Metadata, not magic. Hybrid EXX fractions, range-separation (CAM ω/α/β), VV10 (b/C), and PT2 coefficients are all structured metadata, so a host code can assemble any modern functional from xcx's semilocal part plus its own integrals.
  • A frozen, semver-stable data contract. docs/api-convention.md pins the API, array packing, and metadata semantics.
  • Pure Rust, dependency-light. The published crate depends only on num-dual, nalgebra, and libm. It compiles anywhere Rust does — WASM, embedded targets, cross-compiled HPC nodes — with no C toolchain.

What it does

Given a density (and, depending on the functional, its gradient and/or kinetic energy density), xcx returns the XC energy per particle, its first and second derivatives (vxc and fxc), plus rich metadata (family, input requirements, exact-exchange fraction, range-separation, VV10, and PT2 parameters).

What it does not do (scope fence)

xcx maps `(rho, sigma, tau[, lapl]) → energy density + derivatives + metadata

  • linear mixingand nothing else. It deliberately contains **no** integration grids, atomic-orbital evaluation, SCF driver, or dispersion correction. For hybrids, VV10, and double hybrids it **exposes the parameters** (EXX fraction, CAM ω/α/β, VV10 b/C, PT2 coefficients) so the host electronic-structure code can build those terms;xcx` never computes the exact-exchange, nonlocal- correlation, or perturbative integrals.

See docs/api-convention.md for the full, semver-stable data/ABI contract.

Install

[dependencies]
xcx = "0.3"

MSRV: Rust 1.87.

Quick start

use xcx::{Functional, FunctionalId, Spin, XcInput};

fn main() -> Result<(), xcx::XcError> {
    // Spin-unpolarized LDA exchange over three grid points.
    let f = Functional::new(FunctionalId::LdaX, Spin::Unpolarized)?;
    let rho = [0.1_f64, 0.2, 0.3];
    let out = f.eval(rho.len(), &XcInput::lda(&rho))?;

    // out.exc[i]  = XC energy per particle ε_xc at point i
    // out.vrho[i] = ∂(n·ε_xc)/∂n at point i
    println!("{:?}", out.exc);
    Ok(())
}

Implemented functionals

All with energy, all first derivatives, and second derivatives (fxc), in both spin-polarized and unpolarized modes. The id column is the libxc-compatible numeric id (xcx-private ids ≥ 100000 are marked *).

Family Functional id Notes
LDA lda_x 1 Slater exchange
LDA lda_c_pw 12 PW92 correlation
LDA lda_c_vwn 7 VWN5
LDA lda_c_vwn_3 30 VWN3
LDA lda_c_vwn_rpa 8 VWN5 (RPA)
GGA gga_x_pbe 101 PBE exchange
GGA gga_x_pbe_r 102 revPBE exchange
GGA gga_x_pbe_sol 116 PBEsol exchange
GGA gga_x_rpbe 117 RPBE exchange (exponential enhancement)
GGA gga_c_pbe 130 PBE correlation
GGA gga_c_pbe_sol 133 PBEsol correlation
GGA gga_x_b88 106 Becke 88 exchange
GGA gga_c_lyp 131 Lee–Yang–Parr correlation
GGA gga_c_p86 132 Perdew 86 correlation (PZ81 + gradient term)
GGA gga_xc_b97_3c 327 B97-3c xc part (Becke-97 series refit; host adds D3/SRB)
meta-GGA mgga_x_tpss 202 TPSS exchange
meta-GGA mgga_c_tpss 231 TPSS correlation
meta-GGA mgga_x_r2scan 497 r²SCAN exchange
meta-GGA mgga_c_r2scan 498 r²SCAN correlation
meta-GGA mgga_x_m06_l 203 M06-L exchange
meta-GGA mgga_c_m06_l 233 M06-L correlation
meta-GGA mgga_c_m06_2x 236 M06-2X correlation
meta-GGA mgga_xc_b97m_v 254 B97M-V (semilocal part; VV10 params via metadata)
Hybrid hyb_gga_xc_b3lyp 402 B3LYP — uses VWN_RPA (matching libxc 402)
Hybrid hyb_gga_xc_b3lyp5 475 B3LYP/VWN5
Hybrid hyb_gga_xc_pbeh 406 PBE0
Hybrid hyb_mgga_x_m06_2x 450 M06-2X exchange (54% EXX)
Hybrid hyb_mgga_xc_pw6b95 451 PW6B95 (28% EXX)
Hybrid hyb_gga_xc_pbeh_3c 100005* PBEh-3c xc part (modified-PBE hybrid, 42% EXX; host adds gCP/D3)
RS hybrid hyb_gga_xc_wb97x_v 466 ωB97X-V (SR semilocal part; CAM + VV10 via metadata)
RS hybrid hyb_mgga_xc_wb97m_v 531 ωB97M-V (SR semilocal part; CAM + VV10 via metadata)
Double hybrid hyb_gga_xc_b2plyp 100001* B2PLYP (EXX 0.53; PT2 0.27/0.27 via metadata)
Double hybrid hyb_gga_xc_revdsd_pbep86_d4 100002* revDSD-PBEP86-D4 core (EXX 0.69; PT2 0.5922/0.0636)
Double hybrid hyb_mgga_xc_pwpb95 100003* PWPB95 (EXX 0.50; SOS-PT2 0.269)
Double hybrid hyb_mgga_xc_wb97m_2 100004* ωB97M(2) (CAM + PT2 via metadata; clean-room)

* Functionals with no libxc counterpart (the double hybrids and PBEh-3c) use the xcx-private id namespace (≥ 100000) documented in docs/api-convention.md. xcx emits the scaled semilocal mix; the host adds EXX/CAM and (for double hybrids) the PT2 term from double_hybrid() metadata.

Beyond the named functionals, three parameterized public constructors expose the underlying families as data: Functional::pbe_x(κ, μ, spin), Functional::pbe_c(β, spin) (recover the named PBE-family members at their published parameters), and Functional::b97_xc(c_x, c_ss, c_os, spin) (the Becke-1997 power series, generic over the coefficient count).

The meta-GGA Laplacian (lapl) path is planned via the same AD framework.

Verification

Correctness is checked two ways:

  • Public, dependency-free tests (run in CI, no libxc needed): finite-difference self-consistency of derivatives, polarized/unpolarized consistency, analytic spot-checks, and a fuzz gate asserting finite energy and every derivative component (no NaN/Inf/panic) across the physical input range for all functionals, both spins.
  • Golden cross-check vs. libxc (crates/xcx-validation, never published): where a functional overlaps libxc, values are compared against snapshots generated from a pinned conda-forge libxc (6.1.0) to ≤ 1e-10 relative, plus an end-to-end SCF cross-check on real integration grids. Functionals with no libxc counterpart (the double hybrids) are verified by parameterized re-evaluation through libxc's xc_func_set_ext_params where possible, and by literature reproduction otherwise. The snapshots are committed, so CI runs deterministically without libxc present.

Repository layout

This is a Cargo workspace with two crates:

  • crates/xcx — the published library. Its only dependencies are num-dual, nalgebra, and libm; it carries no C/FFI surface of any kind.
  • crates/xcx-validation — an unpublished (publish = false) crate that cross-checks xcx against the reference C libxc. The libxc FFI (libloading, behind the libxc-ffi feature), the committed reference snapshots, and the snapshot-regeneration tools all live here on purpose, so the published xcx crate stays dependency-light and its packaged artifact can never include test data. See crates/xcx-validation/README.md.

The everyday xcx tests (unit, finite-difference, and the fuzz gate) live in crates/xcx itself and run in CI without libxc.

Interoperability with libxc

Where xcx and libxc implement the same functional, xcx reproduces pinned libxc to ≤ 1e-10 — even where libxc is the less accurate of the two — so results are interchangeable and ids/names can be used drop-in. The handful of intentional, documented numerical divergences (all outside the physically-relevant domain or below the golden tolerance) are described in docs/api-convention.md. Beyond that overlap, xcx is an independent project with its own roadmap, functionals, and metadata model.

License

xcx is licensed per file; every source file carries an SPDX header:

  • Original xcx code — the public API and data-layout layer, the AD family harnesses, the test suites, and all functionals tagged Provenance: clean-room — is dual-licensed under MIT OR Apache-2.0 (LICENSE-MIT, LICENSE-APACHE), at your option — the standard Rust-ecosystem terms.
  • Code derived from libxc (tagged Provenance: ported-from-libxc) remains under the Mozilla Public License 2.0 (LICENSE-MPL), matching upstream.

MPL-2.0 is file-level copyleft, so this scheme is permissive in practice: you may depend on xcx from MIT/Apache/proprietary projects freely; only modifications to the MPL-tagged source files themselves must remain under the MPL. See NOTICE for the full scheme and attribution.

Contributing

See CONTRIBUTING.md. Original contributions are made under MIT OR Apache-2.0 (changes to libxc-derived files stay MPL-2.0); new functionals must be verified per the Verification section.

About

Exchange–correlation (XC) functionals for density-functional theory (DFT) in pure Rust

Topics

Resources

License

Apache-2.0 and 2 other licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
MPL-2.0
LICENSE-MPL

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors