libcint-src 0.6.1

libcint (C library) source or build specification
Documentation

libcint FFI Bindings and Wrapper

This project contains libcint (C language) FFI bindings, wrapper and build-from-source.

libcint is a C library for GTO (gaussian-type orbital) electronic integral, can be applied in computational chemistry, and has already been applied extensively in PySCF.

Resources Badges
Crate Crate
API Document API Documentation
FFI Binding (libcint) v6.2.0
FFI Binding (qcint) v6.2.0
ECP Source Code (PySCF) v2.14.0

This crate is not official bindgen project, neither libcint, PySCF, nor REST. It is originally intended to be some pioneer work for possible future development of rest_libcint wrapper.

Minimal Example

Basic integral calculation

The most important function is CInt::integrate and CInt::integrate_row_major. It is somehow similar to PySCF's mol.intor(intor, aosym, shls_slice), but the user shall check output shape and strides. For more information on usage of crate libcint, we refer to API Documentation.

use libcint::prelude::*;

// This is for testing and development purpose only.
// For actual usage, you should initialize `CInt` with your own molecule data.
let cint_data: CInt = init_h2o_def2_tzvp();

// int1e_ipkin: the integral of kinetic energy operator with derivative on the first orbital

// [mu, nu, comp], column-major, same data memory layout to PySCF's 2/3-center intor
let (out, shape): (Vec<f64>, Vec<usize>) = cint_data.integrate("int1e_ipkin", None, None).into();
assert_eq!(shape, [43, 43, 3]);

// [comp, mu, nu], row-major, same shape to PySCF intor
let (out, shape): (Vec<f64>, Vec<usize>) = cint_data.integrate_row_major("int1e_ipkin", None, None).into();
assert_eq!(shape, [3, 43, 43]);

Building molecule from TOML/JSON (feature bse)

With the bse feature enabled, you can build a CIntMol object directly from TOML or JSON strings, similar to PySCF's gto.M format:

use libcint::prelude::*;

let toml = r#"
atom = "O 0 0 0; H 0 0 0.9572; H 0 0.9266 -0.239987"
basis = "STO-3G"
"#;

let mol = CIntMol::from_toml(toml).unwrap();
let ovlp = mol.cint.integrate("int1e_ovlp", None, None).out.unwrap();

For JSON input:

let json = r#"{"atom": "O 0 0 0; H 0 0 0.9572", "basis": "STO-3G"}"#;
let mol = CIntMol::from_json(json).unwrap();

Supported fields: atom (Cartesian/Z-matrix), basis (BSE name or custom), ecp, unit, cart, ghost_ecp. See from_toml_docs.md for full specification.

Important parts

  • CInt: basic struct defining a molecule (atoms, shells, environment) for electron integrals.
  • CInt::integrate: main integral driver, partly PySCF's mol.intor counterpart (column-major output).
  • CInt::eval_gto: evaluate GTO values on grids (PySCF's mol.eval_ao counterpart).
  • CInt::integrate_row_major: integral driver with row-major output (PySCF shape convention).
  • CInt::integrate_cross: integrals between several CInt instances, useful with auxiliary basis sets.
  • CInt::integrate_with_args, CInt::integrate_args_builder: integral driver with an arguments builder (advanced options such as out, row_major and cintopt).
  • CInt::get_cintopt, CInt::get_cintopt_cross, CIntOpt: build a reusable libcint optimizer (for a single molecule, or from the mols list of a cross integral) and pass it through IntegrateArgs::cintopt / IntorCrossArgs::cintopt, instead of rebuilding it for every call.
  • CIntMol (feature bse): molecule builder from TOML/JSON, a weaker version of PySCF's Mole class.
  • ffi::cint_wrapper and ffi::cecp_wrapper: supported integrators.

Installation and Cargo Features

Install with pre-compiled libcint.so (recommended)

If you have already compiled libcint.so, then put path of this shared library in CINT_DIR or LD_LIBRARY_PATH (or REST_EXT_DIR). Then you just use this library in your Cargo.toml file by

[dependencies]
libcint = { version = "0.6" }

Install and also build-from-source

If you have not compiled libcint.so or libcint.a, then you are suggested to use this library by specifying some cargo features:

[dependencies]
libcint = { version = "0.6", features = ["build_from_source", "static"] }

The source code will be automatically downloaded from github, and cargo will handle the building process.

Build-from-source installs only libcint's library directory into Cargo's output prefix; downloaded sources and intermediate build files remain in the build directory.

If github is not accessible (offline builds, vendored sources), CINT_SRC also accepts a local source directory, a source archive file, or the URL of a source archive of sunqm/libcint or sunqm/qcint; pin an archive with CINT_SRC_SHA256. A git URL or local repository still works as a source mirror (CINT_VER selects the tag). Relative paths are resolved against the libcint-src package directory, and a local git repository is cloned at CINT_VER, so use a non-git directory to build modified sources.

The nested libcint build inherits the CMake toolchain, C compiler and flags, linker flags, target and sysroot settings, and macOS architectures and deployment target from the outer build. This also preserves the compiler and flags selected by the cmake crate for Cargo cross-compilation.

Cargo features

  • Default features: bse (molecule builder from TOML/JSON with Basis Set Exchange support).
  • bse: Enable [CIntMol::from_toml] and [CIntMol::from_json] for building molecules from text input. Requires bse crate for basis set data.
  • api-v6_2: Enable integrals added in libcint v6.2.0 (int1e_ipr_origj, int1e_r_origj_ip, int1e_iprr_origj, int1e_rr_origj_ip, int3c1e_ipip1, int3c1e_ip1ip2, int3c1e_ipvip1, int3c1e_ipip2). This feature is not enabled by default, so the default API stays compatible with libcint/qcint v6.1.2; enable it (e.g. features = ["api-v6_2"]) when using v6.2.0 and needing these integrals. With qcint, it additionally enables the integrals that qcint gained in v6.2.0. For a v6.1.2 source build, set CINT_VER=v6.1.2.
  • build_from_source: Trigger of C language library libcint building. This performs by CMake; source code will be automatically downloaded from github (if environment variable CINT_SRC not specified).
  • static: Use static library for linking. This will require static link libcint.a, and dynamic link libquadmath.so on targets other than macOS (unless no_quadmath).
  • qcint: Use sunqm/qcint instead of sunqm/libcint. Some integrals will not be available if qcint does not supports that. This will also change URL source if cargo feature build_from_source specified.
  • with_f12: Whether F12 integrals (int2e_stg, int2e_yp, etc.) are supported.
  • with_4c1e: Whether 4c1e integrals (int4c1e, etc.) are supported.
  • no_linkage: Disable automatic linkage of libcint in build.rs. By default, this crate will not activate this feature and handle linkage properly. This feature is useful when you want to link libcint manually or use libcint in a different way (probably beneficial to conda, @jeanwsr). However, notice that ECP part will still be built anyway, which will require libcint to be linked properly by your self.
  • no_quadmath: Do not link libquadmath with static, and with build_from_source build libcint without it (libcint then uses long double where it would use __float128). Needed for targets without libquadmath, such as aarch64 Linux.
  • keep_going: With build_from_source, enable libcint's KEEP_GOING option so a Rys quadrature failure only reports an error on stderr and continues, instead of exiting the process. Callers must check the returned integrals for numerical failures. Disabled by default.

Shell environment variables

  • CINT_DIR, LD_LIBRARY_PATH, REST_EXT_DIR: Your compiled library path of libcint.so and libcint.a. This crate will try to find if this library is in directory root, or directory_root/lib. Not searched with cargo feature build_from_source, which links the library it built.
  • CINT_SRC: Source of libcint or qcint: a git repository (URL or local path), a source archive (local path, or URL ending in .tar.gz, .tgz, .tar.bz2, .tar.xz or .zip), or an unpacked source directory. Only works with cargo feature build_from_source.
  • CINT_VER: Version of libcint or qcint (default v6.2.0; must start with v prefix), as a git tag. Only works with cargo feature build_from_source and a git CINT_SRC.
  • CINT_SRC_SHA256: Expected SHA256 of the CINT_SRC archive; the build fails on a mismatch. Only works with cargo feature build_from_source and an archive CINT_SRC; a git source or an unpacked directory with this variable set is rejected.

Low-level optimizer ownership

CIntOptimizer is an opaque owner. Obtain it from CInt::optimizer or CInt::get_optimizer, inspect its family with kind(), and borrow its pointer with as_ptr(). For reuse across calls, prefer CInt::get_cintopt and CIntOpt.

Migration: replace matches on the former CIntOptimizer::Int and CIntOptimizer::Ecp variants with kind(). Code transferring ownership of a raw optimizer must use unsafe { CIntOptimizer::from_raw_int(ptr) } or unsafe { CIntOptimizer::from_raw_ecp(ptr) } and uphold the documented ownership requirements. Constructing an owner from a borrowed pointer is invalid.

50 lines RHF with Rust

This is a full example code to compute the RHF/def2-TZVP energy of H2O molecule, using

  • This crate, libcint, as electronic integral library (corresponding to some parts of pyscf.gto);
  • RSTSR as tensor library (corresponding to NumPy and SciPy).

You will see that except for import and preparation, the code with core algorithms is 27 lines (see also directory examples/h2o_rhf). For comparison, the same code in Python is 23 lines (see also pyscf_h2o_rhf.py).

use libcint::prelude::*;
use rstsr::prelude::*;

pub type Tsr = Tensor<f64, DeviceBLAS>;
pub type TsrView<'a> = TensorView<'a, f64, DeviceBLAS>;

/// Obtain integrals (in row-major, same to PySCF but reverse of libcint)
pub fn intor_row_major(cint_data: &CInt, intor: &str) -> Tsr {
    // use up all rayon available threads for tensor operations
    let device = DeviceBLAS::default();
    // intor, "s1", full_shls_slice
    let (out, shape) = cint_data.integrate(intor, None, None).into();
    // row-major by transposition of col-major shape
    rt::asarray((out, shape.f(), &device)).into_reverse_axes()
}

fn main() {
    let device = DeviceBLAS::default();
    // Assuming H2O/def2-TZVP data for `CInt` has been prepared
    let cint_data = init_h2o_def2_tzvp();

    /* #region rhf total energy algorithms */
    let atom_coords = {
        let coords = cint_data.atom_coords();
        let coords = coords.into_iter().flatten().collect::<Vec<f64>>();
        rt::asarray((coords, &device)).into_shape((-1, 3))
    };
    let atom_charges = rt::asarray((cint_data.atom_charges(), &device));
    let mut dist = rt::sci::cdist((atom_coords.view(), atom_coords.view()));
    dist.diagonal_mut(None).fill(f64::INFINITY);
    let eng_nuc = 0.5 * (&atom_charges * atom_charges.i((.., None)) / dist).sum();
    println!("Nuclear repulsion energy: {eng_nuc}");

    let hcore = intor_row_major(&cint_data, "int1e_kin") + intor_row_major(&cint_data, "int1e_nuc");
    let ovlp = intor_row_major(&cint_data, "int1e_ovlp");
    let int2e = intor_row_major(&cint_data, "int2e");
    let nocc = 5; // hardcoded for H2O, 5 occupied orbitals

    let mut dm = ovlp.zeros_like();
    for _ in 0..40 {
        // hardcoded SCF iterations
        let fock = &hcore + ((1.0_f64 * &int2e - 0.5_f64 * int2e.swapaxes(1, 2)) * &dm).sum_axes([-1, -2]);
        let (_, c) = rt::linalg::eigh((&fock, &ovlp)).into();
        dm = 2.0_f64 * c.i((.., ..nocc)) % c.i((.., ..nocc)).t();
    }
    let eng_scratch = &hcore + ((0.5_f64 * &int2e - 0.25_f64 * int2e.swapaxes(1, 2)) * &dm).sum_axes([-1, -2]);
    let eng_elec = (dm * &eng_scratch).sum();
    println!("Total elec energy: {eng_elec}");
    println!("Total RHF energy: {}", eng_nuc + eng_elec);
    /* #endregion */
}

License

This repository is licensed under Apache-2.0, the same to PySCF.

  • ECP part of code is directly copied and slightly modified from PySCF.
  • Code for filling shell blocks of integrals is modified from an early version of rest_libcint.