Building GPAW in a Python venv on Niflheim

This document explains how to compile a developer version of GPAW on Niflheim. If you just want to run the pre-installed version, please read the guide Using a pre-installed GPAW on Niflheim.

See also

Creating the venv

Download the script and run it like this:

$ python3 <venv-name>

Type python3 --help for help. After a few minutes, you will have a <venv-name> folder with a GPAW installation inside.

In the following, we will assume that your venv folder is ~/venv1/.

The script does the following:

  • load relevant modules from the foss toolchain

  • create the venv

  • clone and install ASE and GPAW from gitlab

  • install some other Python packages from PyPI: sklearn, graphviz, matplotlib, pytest-xdist, myqueue, ase-ext, spglib

  • enable tab-completion for command-line tools: ase, gpaw, mq

Using the venv

The venv needs to be activated like this:

$ source venv1/bin/activate

and you can deactivate it when you no longer need to use it:

$ deactivate

You will want the activation to happen automatically for the jobs you submit to Niflheim. Here are three ways to do it (pick one, and only one):

  1. If you always want to use one venv then just put the activation command in your ~/.bashrc.

  2. If you only want jobs running inside a certain folder to use the venv, then add this to your ~/.bashrc:

    if [[ $SLURM_SUBMIT_DIR/ = $HOME/project-1* ]]; then
        source ~/venv1/bin/activate

    Now, SLURM-jobs submitted inside your ~/project-1/ folder will use the venv.

  3. Use MyQueue. Make sure you have MyQueue version 22.7.0 or later (mq --version). The venv will automatically be activated if it was activated at submit time.

    If you haven’t configured MyQueue then you can do that with this command:

    $ mq config slurm | grep -v sm3090 > ~/.myqueue/

    (skips the sm3090 GPU-enabled nodes).

Adding additional packages

In order to add more Python packages to your venv, you need to activate it and then you can pip install packages. Here is how to install ASR:

$ git clone
$ cd asr
$ git checkout old-master
$ pip install .


Pip may need co compile some code. It is therefore safest to use the thul login node to pip install software as it is the oldest CPU architcture and the other login nodes will understand its code.

Full script

#!/usr/bin/env python3
"""Install gpaw on Niflheim in a virtual environment.

Also installs ase, ase-ext, spglib, sklearn and myqueue.
import argparse
import os
import shutil
import subprocess
from pathlib import Path
from sys import version_info

if version_info < (3, 7):
    raise ValueError('Please use Python-3.7 or later')

version = '3.8'  # Python version in the venv that we are creating

module_cmds_all = """\
module purge
module load GPAW-setups/0.9.20000
module load matplotlib/3.3.3-{tchain}-2020b
module load scikit-learn/0.23.2-{tchain}-2020b
module load pytest-xdist/2.1.0-GCCcore-10.2.0
module load Wannier90/3.1.0-{tchain}-2020b

module_cmds_tc = {
    'foss': """\
module load libxc/4.3.4-GCC-10.2.0
module load libvdwxc/0.4.0-foss-2020b
    'intel': """\
module load libxc/4.3.4-iccifort-2020.4.304

activate_extra = """
export GPAW_SETUP_PATH=$GPAW_SETUP_PATH:{venv}/gpaw-basis-pvalence-0.9.20000

# Set matplotlib backend:
if [[ $SLURM_SUBMIT_DIR ]]; then
    export MPLBACKEND=Agg
    export PYTHONWARNINGS="ignore:Matplotlib is currently using agg"
    export MPLBACKEND=TkAgg

dftd3 = """\
mkdir {venv}/DFTD3
cd {venv}/DFTD3
wget $URL/dftd3.tgz
tar -xf dftd3.tgz
ssh thul ". {venv}/bin/activate && cd {venv}/DFTD3 && make"
ln -s {venv}/DFTD3/dftd3 {venv}/bin

def run(cmd: str, **kwargs) -> subprocess.CompletedProcess:
    return, shell=True, check=True, **kwargs)

def compile_gpaw_c_code(gpaw: Path, activate: Path) -> None:
    """Compile for all architectures: xeon16, xeon24, xeon40, ..."""
    # Remove targets:
    for path in gpaw.glob('build/lib.linux-x86_64-*/_gpaw.*.so'):

    # Compile:
    for host in ['thul', 'sylg', 'svol', 'surt']:
        run(f'ssh {host} ". {activate} && pip install -q -e {gpaw}"')

    # Clean up:
    for path in gpaw.glob('_gpaw.*.so'):
    for path in gpaw.glob('build/temp.linux-x86_64-*'):

def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('venv', help='Name of venv.')
    parser.add_argument('--toolchain', default='foss',
                        choices=['foss', 'intel'],
                        help='Default is foss.')
    parser.add_argument('--dftd3', action='store_true',
                        help='Also build DFTD3.')
    parser.add_argument('--recompile', action='store_true',
                        help='Recompile the GPAW C-extensions in an '
                        'exising venv.')
    args = parser.parse_args()

    if args.toolchain == 'intel':
        raise ValueError('See:')

    venv = Path(args.venv).absolute()
    activate = venv / 'bin/activate'
    gpaw = venv / 'gpaw'

    if args.recompile:
        compile_gpaw_c_code(gpaw, activate)
        return 0

    module_cmds = module_cmds_all.format(tchain=args.toolchain)
    module_cmds += module_cmds_tc[args.toolchain]

    cmds = (' && '.join(module_cmds.splitlines()) +
            f' && python3 -m venv --system-site-packages {args.venv}')


    activate.write_text(module_cmds +

    run(f'. {activate} && pip install --upgrade pip -q')

    packages = ['myqueue',
    run(f'. {activate} && pip install -q ' + ' '.join(packages))

    for name in ['ase', 'gpaw']:
        run(f'git clone -q{name}/{name}.git')

    run(f'. {activate} && pip install -q -e ase/')

    if args.dftd3:
        run(' && '.join(dftd3.format(venv=venv).splitlines()))

    # Compile ase-ext C-extension on old thul so that it works on
    # newer architectures
    run(f'ssh thul ". {activate} && pip install -q ase-ext"')

    run('git clone -q')
    run(f'ssh thul ". {activate} && pip install {venv}/spglib"')

    # Install GPAW:
    siteconfig = Path(

    compile_gpaw_c_code(gpaw, activate)

    for fro, to in [('ivybridge', 'sandybridge'),
                    ('nahelem', 'icelake')]:
        f = gpaw / f'build/lib.linux-x86_64-{fro}-{version}'
        t = gpaw / f'build/lib.linux-x86_64-{to}-{version}'

    # Create .pth file to load correct .so file:
    pth = ('import sys, os; '
           'arch = os.environ["CPU_ARCH"]; '
           f"path = f'{venv}/gpaw/build/lib.linux-x86_64-{{arch}}-{version}'; "

    # Install extra basis-functions:
    run(f'. {activate} && gpaw install-data --basis --version=20000 '
        f'{venv} --no-register')

    extra = activate_extra.format(venv=venv)

    # Tab completion:
    for cmd in ['ase', 'gpaw', 'mq', 'pip']:
        txt = run(f'. {activate} && {cmd} completion' +
                  (' --bash' if cmd == 'pip' else ''),
        extra += txt
    activate.write_text(activate.read_text() + extra)

    # Run tests:
    run(f'. {activate} && ase info && gpaw test')

    return 0

if __name__ == '__main__':
    raise SystemExit(main())