Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Quantum Espresso calculator #325

Merged
merged 22 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
724a246
save before pull
Jul 2, 2024
82182fb
espresso modification for new ase & minhop fix
Jul 2, 2024
f818c77
pytest
Jul 3, 2024
fa63830
change save_calc_results and remove commented line
Jul 3, 2024
9637841
test qe modified
jungsdao Jul 3, 2024
5c8d6ff
save small changes
jungsdao Jul 3, 2024
d0a15f7
change test_remote_run.py
jungsdao Jul 3, 2024
751ae67
patch applied
jungsdao Jul 15, 2024
6eeec7e
QE Tests appear to pass
bernstei Jul 22, 2024
6eb4161
Do QE tests using user's default config file. Update complete_test.t…
bernstei Jul 22, 2024
6a18c9b
undid a change tht messed up keeping track of modified configs results
gelzinyte Jul 22, 2024
ab53d8a
some changes
gelzinyte Jul 22, 2024
c71e03a
Merge branch 'ase_version_qe' of ssh://github.com/libAtoms/workflow i…
bernstei Jul 22, 2024
6486555
keep the reseting of the configs, assuming modification by the calcul…
gelzinyte Jul 29, 2024
f0e33d4
qe_profile_and_pseudo -> qe_pseudo
gelzinyte Jul 29, 2024
69bea74
Merge branch 'main' into ase_version_qe
gelzinyte Jul 29, 2024
1b04a56
Fix pytest.skip in QE test
bernstei Jul 29, 2024
0140cca
Update test_minimahopping.py to be consistent with info/arrays fields…
bernstei Jul 29, 2024
ed14114
github CI create ASE_CONFIG_PATH in default location, so env var isn'…
bernstei Jul 29, 2024
36b59f0
typo in github CI pytest
bernstei Jul 29, 2024
845f2da
Fix github CI ASE config, clean up espresso check pytest skipif
bernstei Jul 29, 2024
6b014c9
Change remote run QE test to use global ASE config for profile
bernstei Jul 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -201,7 +202,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
Loading