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 10 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
14 changes: 14 additions & 0 deletions .github/workflows/pytests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,18 @@ jobs:
ls -l /usr/bin/pw.x
export PYTEST_WFL_ASE_ESPRESSO_COMMAND=pw.x

- name: set up ASE config file
run: |
export config_file_plain=${HOME}/pytest_plain_config.ini
echo "[espresso]" > ${config_file_plain}
echo "command = ${PYTEST_WFL_ASE_ESPRESSO_COMMAND}" >> ${config_file_plain}
echo "pseudo_dir = ${HOME}/pytest_plain" >> ${config_file_plain}

export config_file_cov=${HOME}/pytest_cov_config.ini
echo "[espresso]" > ${config_file_cov}
echo "command = ${PYTEST_WFL_ASE_ESPRESSO_COMMAND}" >> ${config_file_cov}
echo "pseudo_dir = ${HOME}/pytest_cov" >> ${config_file_cov}

- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand All @@ -200,6 +212,7 @@ jobs:
export WFL_NUM_PYTHON_SUBPROCESSES=2
export OMP_NUM_THREADS=1
export WFL_JULIA_COMMAND=${PWD}/julia-1.8.1/bin/julia
export ASE_CONFIG_PATH=${config_file_plain}
pytest --runremote --basetemp $HOME/pytest_plain -rxXs

- name: Test with pytest - coverage
Expand All @@ -217,6 +230,7 @@ jobs:
export WFL_NUM_PYTHON_SUBPROCESSES=2
export OMP_NUM_THREADS=1
export WFL_JULIA_COMMAND=${PWD}/julia-1.8.1/bin/julia
export ASE_CONFIG_PATH=${config_file_cov}
pytest -v --cov=wfl --cov-report term --cov-report html --cov-config=tests/.coveragerc --cov-report term-missing --cov-report term:skip-covered --runremote --basetemp $HOME/pytest_cov -rxXs

# # DEBUGGING
Expand Down
132 changes: 90 additions & 42 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,9 +21,24 @@
from wfl.configset import ConfigSet, OutputSpec
from wfl.autoparallelize import AutoparaInfo

if Version(ase.__version__) < Version("3.23"):
pytest.skip(reason="Quantum espresso tests are only supported for ASE v3.23, please update.")


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

pytest_command = os.environ.get("PYTEST_WFL_ASE_ESPRESSO_COMMAND")
if pytest_command is None and "espresso" in ase_cfg.parser:
pytest_command = EspressoProfile.from_config(ase_cfg, "espresso").command

if (pytest_command is None or os.environ.get('OMP_NUM_THREADS') != "1"):
pytest.skip('No command in PYTEST_WFL_ASE_ESPRESSO_COMMAND or "espresso" configuration '
'or "OMP_NUM_THREADS" is not set to 1.')


@fixture(scope="session")
def qe_cmd_and_pseudo(tmp_path_factory):
def qe_profile_and_pseudo(tmp_path_factory):
"""Quantum Espresso fixture

- checks if pw.x exists (skip otherwise)
Expand All @@ -32,16 +49,12 @@ def qe_cmd_and_pseudo(tmp_path_factory):

Returns
-------
cmd: str
command for pw.x
profile: str
EspressoProfile with correct command and pseudo dir for pytest
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 @@ -55,20 +68,21 @@ def qe_cmd_and_pseudo(tmp_path_factory):
pspot_file = tmp_path_factory.getbasetemp() / "Si.UPF"
shutil_copy(Path(__file__).parent / ".." / "assets" / "QE" / "Si.pz-vbc.UPF", pspot_file)

return cmd, pspot_file
return EspressoProfile(command=pytest_command, pseudo_dir=pspot_file.parent), pspot_file.name

def test_qe_kpoints(tmp_path, qe_cmd_and_pseudo):

qe_cmd, pspot = qe_cmd_and_pseudo
def test_qe_kpoints(tmp_path, qe_profile_and_pseudo):

profile, pspot = qe_profile_and_pseudo

kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot),
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
workdir=tmp_path,
profile=profile
)

# PBC = TTT
Expand All @@ -83,7 +97,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 +118,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 +139,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 +150,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 +169,23 @@ 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
def test_qe_calculation(tmp_path, qe_profile_and_pseudo):

profile, pspot = qe_profile_and_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),
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
)
workdir=tmp_path,
profile=profile
)

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

Expand All @@ -181,8 +194,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,18 +228,18 @@ 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
def test_wfl_Espresso_calc(tmp_path, qe_profile_and_pseudo):

profile, pspot = qe_profile_and_pseudo

atoms = Atoms("Si", cell=(2, 2, 2), pbc=[True] * 3)
kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot),
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)
pseudo_dir=os.path.dirname(pspot),
profile=profile
)

calc = wfl.calculators.espresso.Espresso(
Expand All @@ -239,19 +252,19 @@ 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):
def test_wfl_Espresso_calc_via_generic(tmp_path, qe_profile_and_pseudo):

qe_cmd, pspot = qe_cmd_and_pseudo
profile, pspot = qe_profile_and_pseudo

atoms = Atoms("Si", cell=(2, 2, 2), pbc=[True] * 3)
kw = dict(
pseudopotentials=dict(Si=os.path.basename(pspot)),
pseudopotentials=dict(Si=pspot),
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
workdir=tmp_path,
profile=profile
)

calc = (wfl.calculators.espresso.Espresso, [], kw)
Expand All @@ -265,8 +278,8 @@ 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
)
Expand All @@ -276,3 +289,38 @@ def test_wfl_Espresso_calc_via_generic(tmp_path, qe_cmd_and_pseudo):
assert "qe_calculation_failed" in list(ci)[-1].info


def test_wfl_Espresso_no_explicit_profile(tmp_path, qe_profile_and_pseudo, monkeypatch):
_, pspot = qe_profile_and_pseudo

import ase.config
ase.config.cfg = ase.config.Config.read()

atoms = Atoms("Si", cell=(2, 2, 2), pbc=[True] * 3)
kw = dict(
pseudopotentials=dict(Si=pspot),
input_data={"SYSTEM": {"ecutwfc": 40, "input_dft": "LDA",}},
kpts=(2, 2, 2),
conv_thr=0.0001,
workdir=tmp_path
)

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

cfgs = [atoms]*3 + [Atoms("Cu", cell=(2, 2, 2), pbc=[True]*3)]
ci = ConfigSet(cfgs)
co = OutputSpec()
autoparainfo = AutoparaInfo(
num_python_subprocesses=0
)

ci = generic.calculate(
inputs=ci,
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
10 changes: 10 additions & 0 deletions tests/local_scripts/complete_pytest.tin
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ if [ -d $pytest_dir ]; then
echo "Refusing to run after failing to delete $pytest_dir" 1>&2
exit 1
fi

# set up config file for various (so far just espresso) tests
mkdir -p $pytest_dir
export ASE_CONFIG_PATH=$PWD/pytest_wfl_config.ini
cat << EOF > $ASE_CONFIG_PATH
[espresso]
command = $PYTEST_WFL_ASE_ESPRESSO_COMMAND
pseudo_dir = $pytest_dir
EOF

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`
Expand Down
11 changes: 6 additions & 5 deletions tests/test_remote_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,10 @@ 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")
from ase.config import cfg as ase_cfg
from ase.calculators.espresso import EspressoProfile
profile = EspressoProfile.from_config(ase_cfg, "espresso")

pspot = tmp_path / "Si.UPF"
shutil.copy(Path(__file__).parent / "assets" / "QE" / "Si.pz-vbc.UPF", pspot)

Expand All @@ -208,8 +209,8 @@ 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)
pseudo_dir=str(pspot.parent),
profile=profile
)

calc = (Espresso, [], kw)
Expand Down
Loading
Loading