Using XCFun

To use the library, you will need to:

  • Link your executable to it. Either using the static, libxcfun.a or shared,, version.

  • For C/C++ hosts, include the header file xcfun.h where appropriate:

#include "XCFun/xcfun.h"
  • For Fortran hosts, compile the xcfun.f90 source file together with your sources. This will allow using the Fortran/C interoperability layer with:

use xcfun

Installing using Spack

XCFun can be installed in a Spack environment with:

spack env create myenv
spack env activate myenv
spack install xcfun

Installing using Conda

XCFun can be installed in a Conda environment with:

conda create -n myenv xcfun -c conda-forge
conda activate myenv

The package is built with derivatives up to 8th order and includes the Python bindings.

Integration with your build system

The set up of the build system for you code will change the details on how to achieve the points above. In the following, we provide minimalistic instructions for codes that use either CMake as their build system generator or plain Makefile.

CMake as build system


You can find complete, standalone examples for C, C++, and Fortran in the examples folder.

If you use CMake as your build system, adding the command:

find_package(XCFun CONFIG)

in your CMakeLists.txt will let CMake search for an XCFun installation. CMake will honor the hint variable:


and set up the target XCFun::xcfun for you to link your target against:


For Fortran hosts the xcfun.f90 will have to be compiled too. The following addition suffices:


Other build systems

You will need to set:

  • The linker path:

-L<install-prefix>/lib64 -lxcfun

note that on some systems it might be lib rather than lib64.

  • For C/C++ codes, the include path:

  • For Fortran codes, the location of the Fortran/C interoperability source file xcfun.f90:


Writing an interface


Please, read the full XCFun’s application programming interface documentation for a complete overview.

The library exposes an opaque type, xcfun_t, through which you can obtain the exchange-correlation functional derivatives to the desired order. To do so:

  1. Create one xcfun_t object. There should be only one such object per thread and per XC functional. In C/C++ this is achieved with:

    xcfun_t * fun = xcfun_new();

    whereas in Fortran:

    use, intrinsic :: iso_c_binding
    type(c_ptr) :: fun
    fun = xcfun_new()
  2. The xcfun_t object is now a blank slate. You will need to set the exchange-correlation admixture, i.e. which functional and which amount to use for exchange and correlation. This is achieved with calls to xcfun_set():

    int ierr = 0;
    ierr = xcfun_set(fun, "blyp", 0.9);
    ierr = xcfun_set(fun, "pbec", 0.1);

    We have now set up the BLYP GGA functional.

  3. Next, you will have to set up the evaluation strategy, i.e. which variables will be passed in as input to the functional, which outputs are expected, and the order of the derivatives to return upon evaluation. This can be done by calling xcfun_eval_setup():

    ierr = xcfun_eval_setup(fun, XC_A_B_AX_AY_AX_BX_BY_BZ, XC_PARTIAL_DERIVATIVES, 1);

    The convenience function xcfun_user_eval_setup() is also available. With this set up, we will obtain functional derivatives of the BLYP functional up to first order, using \(\alpha\) and \(\beta\) variables and partial derivatives.

  4. We are now ready to run the evaluation and for this you will have to allocate a properly sized chunk of memory. The function xcfun_output_length() will return how large such a scratch array has to be:

    int nout = xcfun_output_length(fun);
    double * output = malloc(sizeof(double) * nout);
  5. Finally, we proceed to the evaluation. We call xcfun_eval() with an array of density values:

    xcfun_eval(fun, d_elements, output);
  6. The important last step is to clean up the used heap memory. xcfun_delete() is the function to call:


Input, output and units

The library uses atomic units for all input and output variables.

The XC energy density and derivatives can be evaluated using local spin-up \((\alpha)\) and spin-down \((\beta)\) quantities. In the most general case these are:

  • \(n_\alpha\) The spin-up electron number density.

  • \(n_\beta\) The spin-down density.

  • \(\sigma_{\alpha \alpha} = \nabla n_\alpha.\nabla n_\alpha\) The square magnitude of the spin-up density gradient.

  • \(\sigma_{\alpha \beta} = \nabla n_\alpha.\nabla n_\beta\) The dot product between the spin-up and spin-down gradient vectors.

  • \(\sigma_{\beta \beta} = \nabla n_\beta.\nabla n_\beta\) The square magnitude of the spin-down density gradient.

  • \(\tau_\alpha = \frac{1}{2} \sum_i |\psi_{i \alpha}|^2\) The spin-up Kohn-Sham kinetic energy density.

  • \(\tau_\beta\) The spin-down Kohn-Sham kinetic energy density.

Alternatively you can use total density \((n = n_\alpha + n_\beta)\) and spin density \((s = n_\alpha - n_\beta)\) variables. These also have corresponding gradient and kinetic energy components. See xcfun_set() below for more information.

The output is given in graded reverse lexicographical order. For example a spin-polarized second order GGA functional will give 21 output elements, starting with the XC energy density. Symbolically we may write this as a list starting with the energy E, followed by five gradient elements \(E_{\alpha} E_{\beta} E_{\sigma_{\alpha \alpha}} E_{\sigma_{\alpha \beta}} E_{\sigma_{\beta \beta}}\) and 15 second derivatives \(E_{\alpha \alpha} E_{\alpha \beta} E_{\alpha \sigma_{\alpha \alpha}} ... E_{\beta \beta} E_{\beta \sigma_{\alpha \alpha}} ... E_{\sigma_{\beta \beta} \sigma_{\beta \beta}}\) .