Crystal structure

Modules: pyscf.pbc.gto

Initializing a crystal

Initializing a crystal unit cell is very similar to the initialization of a molecule. Instead of the gto.Mole class, one uses the pbc.gto.Cell class to define a cell:

>>> from pyscf.pbc import gto
>>> cell = gto.Cell()
>>> cell.atom = '''H  0 0 0; H 1 1 1'''
>>> cell.basis = 'gth-dzvp'
>>> cell.pseudo = 'gth-pade'
>>> cell.a = numpy.eye(3) * 2
>>> cell.build()

The other two equivalent ways to initialize a molecule introduced in Molecular structure also apply here:

>>> from pyscf.pbc import gto
>>> cell = gto.Cell()
>>> cell.build(
...   atom = '''H  0 0 0; H 1 1 1''',
...   basis = 'gth-dzvp',
...   pseudo = 'gth-pade',
...   a = numpy.eye(3) * 2)

>>> import pyscf
>>> cell = pyscf.M(
...   atom = '''H  0 0 0; H 1 1 1''',
...   basis = 'gth-dzvp',
...   pseudo = 'gth-pade',
...   a = numpy.eye(3) * 2)

>>> from pyscf.pbc import gto
>>> cell = gto.M(
...   atom = '''H  0 0 0; H 1 1 1''',
...   basis = 'gth-dzvp',
...   pseudo = 'gth-pade',
...   a = numpy.eye(3) * 2)

Lattice vectors

The crystal initialization requires an additional parameter a, which represents the lattice vectors. The format of a is array-like:

cell.a = numpy.eye(3) * 2
cell.a = [[2,0,0],[0,2,0],[0,0,2]]

Each row of the 3-by-3 matrix of a represents a lattice vector in Cartesian coordinate, with the same unit as the input atom parameter.

Note

The input lattice vectors (row by row) should form a right-handed coordinate system, as otherwise some integrals may be computed incorrectly in PySCF.

Basis set and pseudopotential

PySCF uses crystalline Gaussian-type orbitals as basis functions for solid calculations. The predefined basis sets and ECPs for molecular calculations can be used in solid calculations as well. In addition, the predefined basis sets include valence basis sets that are optimized for the GTH pseudopotentials (a whole list can be found in pyscf/pbc/gto/basis and pyscf/pbc/gto/pseudo). The input format of basis sets for the Cell object is the same as that for the Mole object.

#!/usr/bin/env python

'''
Input pseudo potential using functions pbc.gto.pseudo.parse and pbc.gto.pseudo.load

It is allowed to mix the Quantum chemistry effective core potentail (ECP) with
crystal pseudo potential (PP).  Input ECP with .ecp attribute and PP with
.pseudo attribute.

See also
pyscf/pbc/gto/pseudo/GTH_POTENTIALS for the GTH-potential format
pyscf/examples/gto/05-input_ecp.py for quantum chemistry ECP format
'''

import numpy
from pyscf.pbc import gto

cell = gto.M(atom='''
Si1 0 0 0
Si2 1 1 1''',
             a = '''3    0    0
                    0    3    0
                    0    0    3''',
             basis = {'Si1': 'gth-szv',  # Goedecker, Teter and Hutter single zeta basis
                      'Si2': 'lanl2dz'},
             pseudo = {'Si1': gto.pseudo.parse('''
Si
    2    2
     0.44000000    1    -6.25958674
    2
     0.44465247    2     8.31460936    -2.33277947
                                        3.01160535
     0.50279207    1     2.33241791
''')},
             ecp = {'Si2': 'lanl2dz'},  # ECP for second Si atom
            )

#
# Some elements have multiple PP definitions in GTH database.  Add suffix in
# the basis name to load the specific PP.
#
cell = gto.M(
    a = numpy.eye(3)*5,
    atom = 'Mg1 0 0 0; Mg2 0 0 1',
    pseudo = {'Mg1': 'gth-lda-q2', 'Mg2': 'gth-lda-q10'})

#
# Allow mixing quantum chemistry ECP (or BFD PP) and crystal PP in the same calculation.
#
cell = gto.M(
    a = '''4    0    0
           0    4    0
           0    0    4''',
    atom = 'Cl 0 0 1; Na 0 1 0',
    basis = {'na': 'gth-szv', 'Cl': 'bfd-vdz'},
    ecp = {'Cl': 'bfd-pp'},
    pseudo = {'Na': 'gthbp'})

#
# ECP can be input in the attribute .pseudo
#
cell = gto.M(
    a = '''4    0    0
           0    4    0
           0    0    4''',
    atom = 'Cl 0 0 1; Na 0 1 0',
    basis = {'na': 'gth-szv', 'Cl': 'bfd-vdz'},
    pseudo = {'Na': 'gthbp', 'Cl': 'bfd-pp'})

Finally, custom basis sets can be defined just like in molecular calculations

#!/usr/bin/env python

'''
Basis can be input the same way as the finite-size system.
'''

#
# Note pbc.gto.parse does not support NWChem format.  To parse NWChem format
# basis string, you need the molecule gto.parse function.
#

import numpy
from pyscf import gto
from pyscf.pbc import gto as pgto
cell = pgto.M(
    atom = '''C     0.      0.      0.    
              C     0.8917  0.8917  0.8917
              C     1.7834  1.7834  0.    
              C     2.6751  2.6751  0.8917
              C     1.7834  0.      1.7834
              C     2.6751  0.8917  2.6751
              C     0.      1.7834  1.7834
              C     0.8917  2.6751  2.6751''',
    basis = {'C': gto.parse('''
# Parse NWChem format basis string (see https://bse.pnl.gov/bse/portal).
# Comment lines are ignored
#BASIS SET: (6s,3p) -> [2s,1p]
O    S
    130.7093200              0.15432897       
     23.8088610              0.53532814       
      6.4436083              0.44463454       
O    SP
      5.0331513             -0.09996723             0.15591627       
      1.1695961              0.39951283             0.60768372       
      0.3803890              0.70011547             0.39195739       
                                ''')},
    pseudo = 'gth-pade',
    a = numpy.eye(3)*3.5668)

Low-dimensional systems

The PySCF pbc module also supports low-dimensional periodic systems. You can initialize the attribute Cell.dimension to specify the dimension of the system:

>>> cell.dimension = 2
>>> cell.a = numpy.eye(3) * 2
>>> cell.build()

When dimension is smaller than 3, a vacuum of infinite size will be applied in certain direction(s). For example, when dimension is 2, the z-direction will be treated as infinitely large and the xy-plane constitutes the periodic surface. When dimension is 1, the y and z axes are treated as vacuum and thus the system is a wire in the x direction. When dimension is 0, all three directions are treated as vacuum, and this is equivalent to a molecular system.

K-points

The k-points used in solid calculations can be obtained through the Cell.make_kpts() method. The minimal input is to specify the number of k-points in each lattice vector direction:

>>> kpts = cell.make_kpts([1,2,2])
>>> print(kpts.shape)
(4,3)

By default, this will return the shifted Monkhorst-Pack mesh which is centered at the \(Gamma\)-point. To get the non-shifted Monkhorst-Pack mesh, one can call:

>>> kpts = cell.make_kpts([1,2,2], with_gamma_point=False)

To get a shifted Monkhorst-pack mesh centered at a specific point, one can call:

>>> kpts = cell.make_kpts([1,2,2], scaled_center=[0.,0.25,0.25])

where scaled_center is defined in the units of lattice vectors.

The obtained k-points are used as input for crystalline electronic structure calculations. For example, k-point sampled RHF can be invoked as follows:

>>> from pyscf.pbc import scf
>>> kpts = cell.make_kpts([2,2,2])
>>> kmf = scf.KRHF(cell, kpts = kpts)
>>> kmf.kernel()

More details about k-point sampling for each method can be found in the corresponding chapters.

Spin

The attribute spin of Cell class has a different meaning to the spin of Mole class. Mole.spin indicates the number of unpaired electrons of a molecule. Most attributes of class Cell represents the information of a unit cell, Cell.spin is used to indicate the number of unpaired electrons of a super cell. In a \(\Gamma\)-point calculation, one can set cell.spin to control the unpaired electrons of the entire system. For calculations with k-point samplings, for example a 2x2x2 k-point calculation, cell.spin=1 leads to one unpaired electron rather than 8 unpaired electrons.

If wrong cell.spin is set, calculations will still run and a warning message for inconsistency between the electron number and spin may be raised.

Currently, the program does not support to assign the unpaired electrons per unit cell. A temporary workaround is to set cell.spin to the product of the number of unpaired electrons and k-points. However, this setting only guarantees that the total numbers of alpha electrons and beta electrons agree to the cell.spin cross all k-points. The occupancies may be different at different k-points.

Other parameters

The attribute precision of Cell object determines the integral accuracy. The default value is 1e-8 hartree. When calling the cell.build() method, some parameters are set automatically based on the value of precision, including:

  • mesh – length-3 list or 1x3 array of int

    • The numbers of grid points in the FFT-mesh in each direction.

  • ke_cutoff – float

    • Kinetic energy cutoff of the plane waves in FFT-DF

  • rcut – float

    • Cutoff radius (in Bohr) of the lattice summation in the integral evaluation

Other attributes of the Mole class such as verbose, max_memory, etc., have the same meanings in the Cell class.

Note

Currently, point group symmetries for crystals are not supported.

Accessing AO integrals

The Mole.intor() method can only be used to evaluate integrals with open boundary conditions. When the periodic boundary conditions of crystalline systems are used, one needs to use the pbc.Cell.pbc_intor() function to evaluate the integrals for short-range operators, such as the overlap and kinetic matrix:

overlap = cell.pbc_intor('int1e_ovlp')
kin = cell.pbc_intor('int1e_kin')

By default, the pbc.Cell.pbc_intor() function only returns the \(\Gamma\)-point integrals. If k-points are specified, it will evaluate the integrals at each k-point:

kpts = cell.make_kpts([2,2,2])  # 2x2x2 Monkhorst-pack mesh
overlap = cell.pbc_intor('int1e_ovlp', kpts=kpts)

Note

pbc.Cell.pbc_intor() can only be used to evaluate the short-range integrals. PBC density fitting methods have to be used to compute the integrals for long-range operators such as nuclear attraction and Coulomb repulsion.

The two-electron Coulomb integrals can be evaluated with the PBC density fitting methods:

from pyscf.pbc import df
eri = df.DF(cell).get_eri()

See Density fitting for crystalline calculations for more details of the PBC density fitting methods.