Modernizing the GPAW code¶
or “Paying down technical debt” or “Future proofing the code”.
Goal:
Have a new version of the code that is
easier to work with for developers (and users)
fully backwards compatible with the old code (also gpw-files)
Agenda:
what is wrong with the old code
the plan
the
gpaw.core
modulelayout of the new code
testing the new code
What is the problem?¶
The
GPAW
object is very big and many other objects inherit from it (LCAOTDDFT
,TDDFT
,AtomPAW
,ExcitedState
,SolvationGPAW
,SJM
).Solution: Split into smaller objects (
ASECalculator
+DFTCalculation
+ …). Use composition instead of inheritance.Wave functions, electron densities and potentials are stored in
numpy.ndarrays
, but what about the associated k-point, the boundary conditions, the unit cell and the parallel distribution?Solution: Use intelligent container objects (
gpaw.core
module).Hamiltonian
too big: Split intoPotential
+Hamiltonian
+PotentialCalculator
.LocalizedFunctionsCollection
: Replace withAtomCenteredFunctions
that has better design.PWDescriptor
is too big: Separate from its real-space buffer and fft-plan.Sane normalization of PW-coefficients.
Sane normalization of occupation numbers (1.0 should mean filled).
Too many
GPAW.get_this(...)
methods: Move to specialized objects.
Transition period¶
Until this project is completed there will be two DFT codes in our Git repository.
Environment variable:
$ GPAW_NEW=1 python script.py
Imports:
from gpaw import GPAW # will look at os.environ['GPAW_NEW']
from gpaw.calculator import GPAW # old GPAW
from gpaw.new.ase_interface import GPAW # new GPAW
The plan¶
Make test-suite (2000+ tests) + AGTS (500+ tests) (“advanced GPAW test-suite” or “big integration tests”) pass:
rewrite things
use wrapper objects (XC-functionals, symmetry-analysis, Poisson-solvers, …)
add thin backwards compatibility layer (make stuff like
calc.wfs.kpt_qs[7][0].P_ani[27]
work)
Clean up:
remove old code
move stuff from
gpaw.new
module to other placesnaming of things?
Refactor wrappers and compatibility layer
More refactoring
Think about new API’s
gpaw.core module¶
Homework for everybody:
read the source: https://gitlab.com/gpaw/gpaw/-/tree/master/gpaw/core
read the docs: https://gpaw.readthedocs.io/documentation/core.html
The gpaw.core
module implements the following objects:
UGArray
, PWArray
,
AtomArrays
, Matrix
and AtomCenteredFunctions
.
Uses in a PAW-context: \(\tilde n_{\sigma}(\mathbf{r})\), \(\tilde v_{\sigma}(\mathbf{r})\), \(\tilde\psi_{n\mathbf{k}\sigma}(\mathbf{r})\), \(D_{\sigma,i_1,i_2}^a\), \(\Delta H_{\sigma,i_1,i_2}^a\), \(Q_{\ell m}^a\), \(P_{\sigma \mathbf{k} ni}^a\), \(\tilde{p}_{\sigma \mathbf{k} i}^a(\mathbf{r}-\mathbf{R}^a)\), \(S_{\mathbf{k}\mu\nu}\), …
Note
All the container objects have a .data
attribute to expose the
numpy.ndarray
(or cupy.ndarray
)
gpaw.core module examples¶
Electron density¶
Represented by a gpaw.core.UGArray
instance
(\(\tilde n_{\sigma}(\mathbf{r})\)):
>>> import numpy as np
>>> from gpaw.core import UGDesc
>>> a = 4.0 # side-length of unit cell
>>> gpts = 20 # number of grid-points
>>> grid = UGDesc(cell=[a, a, a],
... size=(gpts, gpts, gpts)
... # pbc, zerobc, kpt, comm, decomp, dtype
... )
>>> nt_sR = grid.zeros(2, xp=np) # use a numpy array
>>> nt_sR.data.shape
(2, 20, 20, 20)
Note
The container-creation methods (zeros()
, empty()
, new()
, …)
all take dims
, comm
and xp
arguments
Wave functions¶
Represented by a gpaw.core.PWArray
instance as sketched here:
import numpy as np
from gpaw.core import PWDesc
kpt = [0.5, 0.5, 0.25]
pw = PWDesc(gcut=8.0, # G-vector cutoff in bohr^-1
# ecut=..., # plane-wave cutoff in hartree
cell=cell, # bohr
kpt=kpt)
N = 5 # number of electrons
wavefunctions = pw.empty(N)
Atom-arrays¶
PAW atomic density-matrices for an H\(_2\)O molcule (\(D_{\sigma,i_1,i_2}^a\)):
>>> nspins = 2
>>> D_asii = AtomArraysLayout(
... [(5, 5), (5, 5), (13, 13)],
... # dtype=float,
... # xp=np,
... # atomdist=...
... ).zeros(nspins)
>>> type(D_asii)
<class 'gpaw.core.atom_arrays.AtomArrays'>
>>> D_asii.data.shape
(2, 219)
Atom-centered functions¶
PAW-projector functions
(\(\tilde{p}_{\sigma \mathbf{k} i}^a(\mathbf{r}-\mathbf{R}^a)\))
are represented by an
AtomCenteredFunctions
object:
>>> projectors = pw.atom_centered_functions(
... splines,
... positions,
... # atomdist=..., xp=...
... )
>>> type(projectors)
<class 'gpaw.core.pwacf.PWAtomCenteredFunctions'>
>>> P_ani = projectors.integrate(wavefunctions)
>>> type(P_ani)
<class 'gpaw.core.atom_arrays.AtomArrays'>
>>> P_ani.data.shape
(5, 23)
Where is what?¶
calc = GPAW(...)
# old:
nt = calc.density.nt_sG[0]
for kpt in calc.wfs.kpt_u:
f = kpt.f_n
wf = kpt.psit_nG
...
# new:
dft = calc.dft
nt = dft.density.nt_sR.data[0]
for wfs in dft.ibzwfs:
f = wfs.occ_n
wf = wfs.psit_nX.data
# or:
f = wfs.occ_n * wfs.weight * wfs.spin_degeneracy
wf = wfs.psit_nX.data * np.prod(nt.shape)
Full picture:
atoms.calc
:dft
:density
:nt_sR
,D_asii
, …potential
:vt_sR
,dH_asii
, …pot_calc
:xc
poisson_solver
scf_loop
:eigensolver
hamiltonian
occ_calc
mixer
ibzwfs
:IBZWaveFunctions
ibz
:symmetries
bz
wfs_qs[q][s]
:psit_nX
occ_n
…
Testing¶
Currently, 1752 out of 2034 tests pass:
$ GPAW_NEW=1 pytest -m \
"not (old_gpaw_only or dscf or gllb or \
ofdft or do or legacy or lrtddft or \
rttddft or hybrids or pipekmezey) or new_gpaw_ready"
Pytest marks:
old_gpaw_only
new_gpaw_ready
legacy
Pytest fixture:
gpaw_new: bool
Calculator object attribute:
calc.old: bool
Final slide¶
Try out new GPAW and let us know how/if it worked
To do: OFDFT, LRTDDFT(1+2), GLLBSC, core-holes, QNA, TB09, LB94, Old DSCF, SIC(1+2), …
Let me know if you would like to help out:
porting code
fixing tests
writing documentation
Try out some new features (only in new GPAW):
Spin-spirals.
Reuse of wave functions after cell-changes (
gpaw.core.PWArray.morph()
).GPU implementation. Look for
self.xp
in the code (numpy
orcupy
).Faster and less memory hungry Poisson-solver for PW-mode: P. E. Blöchl, PRB 50, 17953 (1994).