Skip to content

Commit

Permalink
Merge pull request #325 from libAtoms/ase_version_qe
Browse files Browse the repository at this point in the history
Update Quantum Espresso calculator
  • Loading branch information
gelzinyte authored Jul 30, 2024
2 parents aa1411e + 6b014c9 commit 5758f8c
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 132 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/pytests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
fi
source $mklvars intel64
# pip constraint needs to be an absolute filename
export PIP_CONSTRAINT=$PWD/$PIP_CONSTRAINT
git clone https://github.com/phonopy/phonopy
Expand Down Expand Up @@ -199,7 +200,15 @@ jobs:
echo "which pw.x"
which pw.x
ls -l /usr/bin/pw.x
export PYTEST_WFL_ASE_ESPRESSO_COMMAND=pw.x
espresso_command=pw.x
mkdir -p ${HOME}/.config/ase/
echo "[espresso]" >> ${HOME}/.config/ase/config.ini
echo "command = ${espresso_command}" >> ${HOME}/.config/ase/config.ini
echo "pseudo_dir = ${HOME}/dummy" >> ${HOME}/.config/ase/config.ini
echo 'post-espresso $HOME/.config/ase/config.ini'
cat $HOME/.config/ase/config.ini
- name: Lint with flake8
run: |
Expand Down
115 changes: 64 additions & 51 deletions tests/calculators/test_qe.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from shutil import which, copy as shutil_copy
from pathlib import Path
import pytest
from packaging.version import Version

import ase.io
import numpy as np
import requests
from ase import Atoms
from ase.build import bulk
from ase.calculators.espresso import EspressoProfile
from pytest import approx, fixture, raises, skip

# from wfl.calculators.espresso import evaluate_autopara_wrappable, qe_kpoints_and_kwargs
Expand All @@ -19,29 +21,36 @@
from wfl.configset import ConfigSet, OutputSpec
from wfl.autoparallelize import AutoparaInfo

ase_version = pytest.mark.skipif(Version(ase.__version__) < Version("3.23"),
reason="Quantum espresso tests are only supported for ASE v3.23, "
f"please update from {ase.__version__}.")

from ase.config import cfg as ase_cfg
from ase.calculators.espresso import EspressoProfile

# do all tests using user's default config file
# pseudo_dir will be overridden whenever calculator is constructed to ensure that
# pytest-specific PPs are used
espresso_avail = pytest.mark.skipif(not ("espresso" in ase_cfg.parser and os.environ.get('OMP_NUM_THREADS') == "1"),
reason='No "espresso" ASE configuration or '
f'"OMP_NUM_THREADS={os.environ.get("OMP_NUM_THREADS")}" is not set to 1.')


@fixture(scope="session")
def qe_cmd_and_pseudo(tmp_path_factory):
def qe_pseudo(tmp_path_factory):
"""Quantum Espresso fixture
- checks if pw.x exists (skip otherwise)
- downloads a pseudo-potential for Si
- copies a pseudo-potential for Si
implementation based on:
https://stackoverflow.com/questions/63417661/pytest-downloading-a-test-file-once-and-using-it-for-multiple-tests
Returns
-------
cmd: str
command for pw.x
pspot_file: str
Si pseudo potential file
Si pseudo potential file name
"""

cmd = os.environ.get("PYTEST_WFL_ASE_ESPRESSO_COMMAND")
if cmd is None:
skip("no PYTEST_WFL_ASE_ESPRESSO_COMMAND to specify executable")

# originally downloaded from here, but broken due to need for account/license click
# url = "https://www.quantum-espresso.org/upf_files/Si.pbe-n-kjpaw_psl.1.0.0.UPF"
# replaced with this
Expand All @@ -53,27 +62,29 @@ def qe_cmd_and_pseudo(tmp_path_factory):

# write to a temporary file
pspot_file = tmp_path_factory.getbasetemp() / "Si.UPF"
shutil_copy(Path(__file__).parent / ".." / "assets" / "QE" / "Si.pz-vbc.UPF", pspot_file)
shutil_copy(Path(__file__).parent.parent / "assets" / "QE" / "Si.pz-vbc.UPF", pspot_file)

return cmd, pspot_file
return pspot_file

def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):

qe_cmd, pspot = qe_cmd_and_pseudo
@ase_version
@espresso_avail
def test_qe_kpoints(tmp_path, qe_pseudo):

pspot = qe_pseudo

kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot.name),
pseudo_dir=pspot.parent,
input_data={"SYSTEM": {"ecutwfc": 40, "input_dft": "LDA",}},
kpts=(2, 3, 4),
conv_thr=0.0001,
calculator_exec=qe_cmd,
pseudo_dir=os.path.dirname(pspot),
workdir=tmp_path
)
)

# PBC = TTT
atoms = Atoms("H", cell=[1, 1, 1], pbc=True)
properties = ["energy", "stress"]
properties = ["energy", "stress"]
calc = wfl.calculators.espresso.Espresso(**kw)
calc.atoms = atoms.copy()
calc.setup_calc_params(properties)
Expand All @@ -83,7 +94,7 @@ def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):

# PBC = FFF
atoms = Atoms("H", cell=[1, 1, 1], pbc=False)
properties = ["energy", "stress", "forces"]
properties = ["energy", "stress", "forces"]
## removing stress here to duplicate what calculators.generic would do
properties.remove("stress")
##
Expand All @@ -104,7 +115,7 @@ def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):

# PBC mixed -- kpts
atoms = Atoms("H", cell=[1, 1, 1], pbc=[True, False, False])
properties = ["energy", "stress", "forces"]
properties = ["energy", "stress", "forces"]
kw["koffset"] = True
calc = wfl.calculators.espresso.Espresso(**kw)
calc.atoms = atoms.copy()
Expand All @@ -125,8 +136,8 @@ def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):

# koffset in mixed PBC
atoms = Atoms("H", cell=[1, 1, 1], pbc=[True, False, False])
properties = ["energy", "forces"]
kw["koffset"] = False
properties = ["energy", "forces"]
kw["koffset"] = False
calc = wfl.calculators.espresso.Espresso(**kw)
calc.atoms = atoms.copy()
calc.setup_calc_params(properties)
Expand All @@ -136,7 +147,7 @@ def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):

# PBC mixed -- kspacing
atoms = Atoms("H", cell=[1, 1, 1], pbc=[True, False, False])
properties = ["energy", "stress", "forces"]
properties = ["energy", "stress", "forces"]
kw["kspacing"] = 0.1
kw["koffset"] = (0, 1, 0)
calc = wfl.calculators.espresso.Espresso(**kw)
Expand All @@ -155,24 +166,25 @@ def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):
assert calc.parameters["koffset"] == (0, 0, 0)


def test_qe_calculation(tmp_path, qe_cmd_and_pseudo):
# command and pspot
qe_cmd, pspot = qe_cmd_and_pseudo
@ase_version
@espresso_avail
def test_qe_calculation(tmp_path, qe_pseudo):

pspot = qe_pseudo

# atoms
at = bulk("Si")
at.positions[0, 0] += 0.01
at0 = Atoms("Si", cell=[6.0, 6.0, 6.0], positions=[[3.0, 3.0, 3.0]], pbc=False)

kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot.name),
pseudo_dir=pspot.parent,
input_data={"SYSTEM": {"ecutwfc": 40, "input_dft": "LDA",}},
kpts=(2, 2, 2),
conv_thr=0.0001,
calculator_exec=qe_cmd,
pseudo_dir=os.path.dirname(pspot),
workdir=tmp_path
)
)

calc = (wfl.calculators.espresso.Espresso, [], kw)

Expand All @@ -181,8 +193,8 @@ def test_qe_calculation(tmp_path, qe_cmd_and_pseudo):

results = generic.calculate(
inputs=[at0, at],
outputs=c_out,
calculator=calc,
outputs=c_out,
calculator=calc,
output_prefix='QE_',
)

Expand Down Expand Up @@ -215,22 +227,24 @@ def test_qe_calculation(tmp_path, qe_cmd_and_pseudo):
assert si2.arrays["QE_forces"][0] == approx(-1 * si2.arrays["QE_forces"][1])


def test_wfl_Espresso_calc(tmp_path, qe_cmd_and_pseudo):
# command and pspot
qe_cmd, pspot = qe_cmd_and_pseudo
@ase_version
@espresso_avail
def test_wfl_Espresso_calc(tmp_path, qe_pseudo):

pspot = qe_pseudo

atoms = Atoms("Si", cell=(2, 2, 2), pbc=[True] * 3)
kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot.name),
pseudo_dir=pspot.parent,
input_data={"SYSTEM": {"ecutwfc": 40, "input_dft": "LDA",}},
kpts=(2, 2, 2),
conv_thr=0.0001,
calculator_exec=qe_cmd,
pseudo_dir=os.path.dirname(pspot)
)
conv_thr=0.0001
)

calc = wfl.calculators.espresso.Espresso(
workdir=tmp_path,
keep_files=True,
**kw)
atoms.calc = calc

Expand All @@ -239,20 +253,21 @@ def test_wfl_Espresso_calc(tmp_path, qe_cmd_and_pseudo):
atoms.get_stress()


def test_wfl_Espresso_calc_via_generic(tmp_path, qe_cmd_and_pseudo):
@ase_version
@espresso_avail
def test_wfl_Espresso_calc_via_generic(tmp_path, qe_pseudo):

qe_cmd, pspot = qe_cmd_and_pseudo
pspot = qe_pseudo

atoms = Atoms("Si", cell=(2, 2, 2), pbc=[True] * 3)
kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot.name),
pseudo_dir=pspot.parent,
input_data={"SYSTEM": {"ecutwfc": 40, "input_dft": "LDA",}},
kpts=(2, 2, 2),
conv_thr=0.0001,
calculator_exec=qe_cmd,
pseudo_dir=os.path.dirname(pspot),
workdir=tmp_path
)
)

calc = (wfl.calculators.espresso.Espresso, [], kw)

Expand All @@ -265,14 +280,12 @@ def test_wfl_Espresso_calc_via_generic(tmp_path, qe_cmd_and_pseudo):

ci = generic.calculate(
inputs=ci,
outputs=co,
calculator=calc,
outputs=co,
calculator=calc,
output_prefix='qe_',
autopara_info=autoparainfo
)

ats = list(ci)
assert not any("qe_calculation_failed" in at.info for at in ats[:-1])
assert "qe_calculation_failed" in list(ci)[-1].info


6 changes: 4 additions & 2 deletions tests/local_scripts/complete_pytest.tin
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export ASE_VASP_COMMAND_GAMMA=vasp.gamma.serial
export PYTEST_VASP_POTCAR_DIR=$VASP_PATH/pot/rev_54/PBE
# QE
module load dft/pwscf
export PYTEST_WFL_ASE_ESPRESSO_COMMAND="env MPIRUN_EXTRA_ARGS='-np 1' pw.x"
# no ORCA

export OPENBLAS_NUM_THREADS=1
Expand All @@ -58,14 +57,17 @@ if [ -d $pytest_dir ]; then
echo "Refusing to run after failing to delete $pytest_dir" 1>&2
exit 1
fi

mkdir -p $pytest_dir

pytest -v -s --basetemp $pytest_dir ${runremote} --runslow --runperf -rxXs "$@" >> complete_pytest.tin.out 2>&1

l=`egrep '^=.*(passed|failed|skipped|xfailed)' complete_pytest.tin.out`

echo "summary line $l"
lp=$( echo $l | sed -E -e 's/ in .*//' -e 's/\s*,\s*/\n/g' )

declare -A expected_n=( ["passed"]="176" ["skipped"]="21" ["warnings"]=823 ["xfailed"]=2 ["xpassed"]=1 )
declare -A expected_n=( ["passed"]="175" ["skipped"]="21" ["warnings"]=823 ["xfailed"]=2 ["xpassed"]=1 )
IFS=$'\n'
for out in $lp; do
out_n=$(echo $out | sed -e 's/^=* //' -e 's/ .*//' -e 's/,//')
Expand Down
8 changes: 4 additions & 4 deletions tests/test_minimahopping.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ def test_return_md_traj(cu_slab, tmp_path):
atoms_opt = minimahopping.minimahopping(inputs, outputs, calc, fmax=1, totalsteps=5, save_tmpdir=True, return_all_traj=True,
rng=np.random.default_rng(1), workdir=tmp_path)

assert any(["minima" in at.info["config_type"] for at in atoms_opt])
assert any(["traj" in at.info["config_type"] for at in atoms_opt])
assert any(["minhop_min" in at.info["config_type"] for at in atoms_opt])
assert any(["minhop_traj" in at.info["config_type"] for at in atoms_opt])


def test_mult_files(cu_slab, tmp_path):
Expand Down Expand Up @@ -82,8 +82,8 @@ def test_relax(cu_slab, tmp_path):
assert 1 <= len(list(ats)) <= totalsteps

atoms_opt = list(atoms_opt)
assert all(['minima' in at.info['config_type'] for at in atoms_opt])
assert all(['minhop_min' in at.info['config_type'] for at in atoms_opt])

for at in atoms_opt:
force_norms = np.linalg.norm(at.get_forces(), axis=1)
force_norms = np.linalg.norm(at.arrays["last_op__minhop_forces"], axis=1)
assert all(force_norms <= fmax)
4 changes: 0 additions & 4 deletions tests/test_remote_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,6 @@ def do_generic_calc_qe(tmp_path, sys_name, monkeypatch, remoteinfo_env):
'resources': {'max_time': '1h', 'num_nodes': 1},
'num_inputs_per_queued_job': -36, 'check_interval': 10}

qe_cmd = os.environ.get("PYTEST_WFL_ASE_ESPRESSO_COMMAND")
if qe_cmd is None:
pytest.skip("no PYTEST_WFL_ASE_ESPRESSO_COMMAND to specify executable")
pspot = tmp_path / "Si.UPF"
shutil.copy(Path(__file__).parent / "assets" / "QE" / "Si.pz-vbc.UPF", pspot)

Expand All @@ -208,7 +205,6 @@ def do_generic_calc_qe(tmp_path, sys_name, monkeypatch, remoteinfo_env):
input_data={"SYSTEM": {"ecutwfc": 40, "input_dft": "LDA",}},
kpts=(2, 2, 2),
conv_thr=0.0001,
calculator_exec=qe_cmd,
pseudo_dir=str(pspot.parent)
)

Expand Down
2 changes: 0 additions & 2 deletions wfl/calculators/aims.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
FHI-Aims Calculator
"""

import shlex

from copy import deepcopy
import numpy as np

Expand Down
Loading

0 comments on commit 5758f8c

Please sign in to comment.