# Density fitting (DF)#

*Modules*: `pyscf.df`

, `pyscf.pbc.df`

## Introduction#

Density fitting (DF), sometimes also called the resolution of identity (RI) approximation, is a method to approximate the four-index electron repulsion integrals (ERIs) by two- and three-index tensors. [54] In DF, the atomic orbital (AO) product space is expanded in terms of an auxiliary basis set. PySCF has extensive support for using DF in both molecular and crystalline calculations. This section covers the basic usage of DF in molecular applications. See Density fitting for crystalline calculations for the use of DF in periodic calculations.

## Using DF in SCF calculations#

DF is not used by default when initializing a `SCF`

or `MCSCF`

object, but can be invoked via the `density_fit()`

method as follows:

```
from pyscf import gto, scf, mcscf
mol = gto.M(atom='N 0 0 0; N 0 0 1.2', basis='def2-tzvp')
mf = scf.RHF(mol).density_fit().run() # output: -108.943773290737
mc = mcscf.CASSCF(mf, 8, 10).density_fit().run() # output: -109.077600181849
```

Alternatively, one can initialize a `DF`

object and overwrite the `with_df`

attribute of the `SCF`

object:

```
from pyscf import gto, scf, df
mol = gto.M(atom='N 0 0 0; N 0 0 1.2', basis='def2-tzvp')
mydf = df.DF(mol).build()
mf = scf.RHF(mol).density_fit()
mf.with_df = mydf
mf.kernel() # output: -108.943773290737
```

Setting `with_df`

to `None`

turns DF off.

## Using DF in post-SCF calculations#

A post-SCF calculation will use DF automatically if the preceding SCF calculation is performed using DF. The example below performs a TDDFT/TDA calculation using DF:

```
from pyscf import gto, dft, tddft
mol = gto.M(atom='N 0 0 0; N 0 0 1.2', basis='def2-tzvp')
mf = dft.RKS(mol, xc="b3lyp").density_fit().run()
td = tddft.TDA(mf).run()
print(td.e) # output: [0.27294247 0.28837975 0.2883798 ]
```

Alternatively, one can use the `density_fit()`

method provided by most post-SCF classes in the same way as in a SCF calculation described above. This is useful when one wants to use DF to accelerate only the post-SCF calculations or to use a different auxiliary basis for the post-SCF calculations than that used by SCF. The example below performs a DF-MP2 calculation using the `cc-pvtz-ri`

basis [55] on top of a SCF calculation that does not use DF:

```
from pyscf import gto, scf, mp
mol = gto.M(atom='N 0 0 0; N 0 0 1.2', basis='cc-pvtz')
mf = scf.RHF(mol).run()
mmp = mp.MP2(mf).density_fit(auxbasis="cc-pvtz-ri")
e_corr = mmp.kernel()[0]
print(e_corr) # output: -0.4288734824009443
```

## Choice of auxiliary basis#

### General considerations#

The choice of auxiliary basis depends on the AO basis. By default, PySCF uses a pre-defined auxiliary basis set optimized for the used AO basis set, if one exists. This includes many commonly used AO basis sets in electronic structure calculations, e.g., the Ahlrichs def2 family, [56, 57] and the Dunning cc family, [58].

When a pre-defined auxiliary basis set is not available, an even-tempered basis (ETB) set is generated by the following rule

where both \(\alpha_l\) and \(n_l\) are determined automatically from the AO basis. Specifically, \(\alpha_l\) is set to the *smallest* exponent of the AO products with angular momentum \(l\), and \(n_l\) is chosen so that the *largest* exponent of the AO products with angular momentum \(l\) lies in between \(\alpha_l \beta^{n_l-1}\) and \(\alpha_l \beta^{n_l}\). The parameter \(\beta\) controls the “density” of the fitting functions: smaller \(\beta\) gives an ETB of larger size and *vice versa*. The default is \(\beta = 2.0\).

The user can overwrite the default choice of the auxiliary basis by setting `auxbasis`

upon initialization or at a later stage:

```
from pyscf import gto, scf, df
mol = gto.M(atom='N 0 0 0; N 0 0 1.2', basis='cc-pvdz')
mf = scf.RHF(mol).density_fit(auxbasis="weigend")
mf.kernel() # -108.910953335055
mf.with_df.auxbasis = "cc-pvdz-jkfit" # this is the default
mf.kernel() # -108.913710743723
mf.with_df.auxbasis = df.aug_etb(mol, beta=1.7) # ETB with beta = 1.7
mf.kernel() # -108.914059329528
```

The user can print the used auxiliary basis set at any time by:

```
print(mf.with_df.auxmol.basis)
```

More examples on using customized auxiliary basis can be found in examples/df/01-auxbasis.py.

### Special considerations for DFT#

For DFT calculations with pure exchange-correlation functionals (i.e., LDA and GGA), the default auxiliary basis, which is designed for fitting both the Coulomb and the Hartree-Fock exchange integrals, may be unnecessarily large. We recommend using the `def2-universal-jfit`

basis [59] for a more cost-effective choice as shown in the following example:

```
from pyscf import gto, dft
mol = gto.M(atom='N 0 0 0; N 0 0 1.2', basis='def2-tzvpp')
mf = dft.RKS(mol, xc="pbe").density_fit().run() # -109.432329411505
print(mf.with_df.auxmol.basis) # default: def2-tzvpp-jkfit
print(mf.with_df.auxmol.nao_nr()) # 154 aux basis functions
mf.with_df.auxbasis = "def2-universal-jfit" # same as "weigend"
mf.kernel() # -109.432334646585
print(mf.with_df.auxmol.nao_nr()) # 98 aux basis functions
```

## Saving and reusing DF tensors#

A key result of the `DF`

class is the Cholesky decomposition of the ERIs (CDERIs) in terms of the 3-index tensor \(d_{L\mu\nu}\),

The `build()`

method of `DF`

computes the CDERIs.
By default, the 3-index tensor defining the CDERIs is discarded once the calculation finishes. Sometimes it is useful to save it to disk for re-use in later calculations. This can be achieved by specifying a HDF5 file by setting `_cderi_to_save`

either at the SCF stage:

```
mf = scf.RHF(mol).density_fit()
mf.with_df._cderi_to_save = 'saved_cderi.h5'
mf.kernel()
```

or by initializing a `DF`

object separately:

```
mydf = df.DF(mol)
mydf.auxbasis = df.make_auxbasis(mol)
mydf._cderi_to_save = 'saved_cderi.h5'
mydf.build()
```

The saved DF tensor can be used later by setting `_cderi`

to the HDF5 file:

```
mf = scf.RHF(mol).density_fit()
mf.with_df._cderi = 'saved_cderi.h5'
mf.kernel()
```

More examples on saving and using DF integrals and tensors can be found in examples/df/10-access_df_integrals.py, examples/df/11-access_df_tensor.py, and examples/df/40-precompute_df_integrals.py.

## Advanced examples#

More advanced examples of using the `df`

module include

Computing the \(\mathbf{J}\)-matrix of DFT in an I/O-free manner: examples/df/11-get_j_io_free.py.

Using DF integrals to define the Hamiltonian of a CASSCF calculation: examples/df/40-custom_df_hamiltonian.py.

Generating analytical gradients for DF integrals: examples/df/41-df_integrals_gradients.py.

Customizing the

`get_jk()`

method of a SCF class using DF: examples/df/42-overwrite_get_jk.py.