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

Add support for per-atomic-config calculator in generic.calculator. #254

Merged
merged 23 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5b5d2a4
Add support for per-atomic-config calculator in generic.calculator.
bernstei Jul 26, 2023
07e3b33
Typos in new per-config calculator params
bernstei Jul 27, 2023
494d382
debug of pickeld calculator
Felixrccs Aug 8, 2023
a23ce65
calculator default construction
Aug 15, 2023
ebfe2fb
Merge pull request #256 from Felixrccs/autopara_calc_params
bernstei Aug 15, 2023
a4cfac2
Merge branch 'main' into autopara_calc_params
bernstei Aug 15, 2023
e84cbaf
logo files from Olga Vinogradova <[email protected]>. they are f…
gabor1 Aug 4, 2023
e57e8c6
Update README.md with logo
gabor1 Aug 4, 2023
e6254ae
add wfl logo to the docs
gelzinyte Aug 17, 2023
73f03ec
Merge branch 'autopara_calc_params' of ssh://github.com/libAtoms/work…
bernstei Aug 18, 2023
7eb2702
Merge branch 'main' into autopara_calc_params
bernstei Aug 18, 2023
175d486
per configuration pytest
Aug 21, 2023
2bbde81
try to dedug vasp pytest
Aug 22, 2023
76282b9
LJ/Morse-based per config pytest
Sep 6, 2023
5241f4f
switch Morse to EMT
Sep 6, 2023
6f8cfa4
Revert "try to dedug vasp pytest"
Sep 6, 2023
1e195f4
Merge pull request #261 from Felixrccs/autopara_calc_params
bernstei Sep 6, 2023
c1d28ee
Debug of test
Felixrccs Sep 22, 2023
2c94665
Merge branch 'autopara_calc_params' of github:Felixrccs/workflow into…
Felixrccs Sep 22, 2023
1d8756a
debug
Felixrccs Sep 22, 2023
053e0e7
Merge branch 'libAtoms:autopara_calc_params' into autopara_calc_params
Felixrccs Sep 22, 2023
cd3453e
Merge pull request #265 from Felixrccs/autopara_calc_params
bernstei Sep 22, 2023
279d6b3
Cleanup of new per-config calculator VASP test
bernstei Sep 22, 2023
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
12 changes: 12 additions & 0 deletions tests/calculators/test_calc_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ def test_default_properties():
assert "dummy_forces" in mol_out.arrays.keys()


def test_config_specific_calculator(tmp_path):
mol_in = [molecule("CH4"), molecule("CH4"), molecule("CH4")]
mol_in[1].info["WFL_CALCULATOR_KWARGS"] = {'epsilon':2.0}
mol_in[2].info["WFL_CALCULATOR_INITIALIZER"] = EMT
calculator = [LennardJones, [], {}]
mol_out = generic.calculate(mol_in, OutputSpec(tmp_path / "run.xyz"), calculator, properties=["energy", "forces"], output_prefix="dummy_")

energies = []
for at in mol_out:
energies.append(at.info['dummy_energy'])
assert energies[0] == energies[1]/2 != energies[2]

####################################################################################################

class EMT_override_def_autopara(EMT):
Expand Down
41 changes: 41 additions & 0 deletions tests/calculators/test_vasp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
import glob
import os
import copy

import numpy as np

Expand Down Expand Up @@ -250,3 +251,43 @@ def test_vasp_scratchdir(tmp_path, monkeypatch):
assert 'TEST_energy' in ats[0].info
assert 'TEST_forces' in ats[0].arrays
# ase.io.write(sys.stdout, list(configs_eval), format='extxyz')



def test_vasp_per_configuration(tmp_path):
vasp_kwargs = {
"encut": 200.0, # kinetic energy cutoff
"ediff": 1.0e-3,
"kspacing": 1.0,
"pp": os.environ['PYTEST_VASP_POTCAR_DIR'],
}

atoms = [Atoms('Si', cell=(2, 2, 2), pbc=[True] * 3), Atoms('Si', cell=(2, 2, 2), pbc=[True] * 3), Atoms('Si', cell=(2, 2, 2), pbc=[True] * 3)]

tmp = copy.deepcopy(vasp_kwargs)
tmp['encut'] = 220.0
atoms[1].info["WFL_CALCULATOR_INITIALIZER"] = Vasp
atoms[1].info["WFL_CALCULATOR_KWARGS"] = tmp

tmp = copy.deepcopy(vasp_kwargs)
tmp['encut'] = 240.0
atoms[2].info["WFL_CALCULATOR_KWARGS"] = tmp

calculator = (Vasp, [], vasp_kwargs)

configs_eval = generic.calculate(
inputs=ConfigSet(tmp_path / 'vasp_in.xyz'),
bernstei marked this conversation as resolved.
Show resolved Hide resolved
outputs=OutputSpec('vasp_out.regular.xyz', file_root=tmp_path),
calculator=calculator,
output_prefix='TEST_')

ats = list(configs_eval)

with open(os.path.join(tmp_path,ats[2]), 'r') as fo:
for i in fo.readlines():
if i.split()[0] == 'ENCUT':
assert float(i.split()[-1]) == 240.0

assert ats[0].info['TEST_energy'] > ats[1].info['TEST_energy'] > ats[2].info['TEST_energy']
# import sys
# ase.io.write(sys.stdout, list(configs_eval), format='extxyz')
46 changes: 41 additions & 5 deletions wfl/calculators/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,27 @@


def _run_autopara_wrappable(atoms, calculator, properties=None, output_prefix='_auto_', verbose=False, raise_calc_exceptions=False):
"""evaluates configs using an arbitrary calculator and store results in SinglePointCalculator
"""evaluates configs using an arbitrary calculator and store results in info/arrays entries
or `SinglePointCalculator`.

Defaults to wfl_num_inputs_per_python_subprocess=10, to avoid recreating the calculator for
each configuration, unless calculator class defines a wfl_generic_default_autopara_info
attribute in which case that value is used for the default.

If `Atoms.info` contains 'WFL\_CALCULATOR\_INITIALIZER', 'WFL\_CALCULATOR\_ARGS' or
'WFL\_CALCULATOR\_KWARGS', an individual calculator will be created for that `Atoms` object.
The `initializer` and `*args` will be _overridden_ by the corresponding `Atoms.info` entries, but
the `**kwargs` will be _modified_ (`dict.update`) by the `Atoms.info` entry.

Parameters
----------
atoms: ase.atoms.Atoms / list(Atoms)
input configuration(s)
calculator: Calculator / (initializer, args, kwargs)
ASE calculator or routine to call to create calculator
calculator: Calculator / (initializer (callable), args (list), kwargs (dict))
ASE calculator or routine to call to create calculator. If 'WFL\_CALCULATOR\_ARGS'
`...\_INITIALIZER`, or `...\_KWARGS` are present in any `Atoms.info` dicts, calculator
_must_ be a 3-tuple so that those `initializer`, `*args` or `**kwargs` can be used to
override defaults.
properties: list(str), default ['energy', 'forces', stress']
Properties to request from calculator. If any are not present after calculation (e.g.
stress for nonperiodic configurations), a warning will be printed.
Expand All @@ -36,14 +45,41 @@ def _run_autopara_wrappable(atoms, calculator, properties=None, output_prefix='_

if properties is None:
properties = ['energy', 'forces', 'stress']
calculator = construct_calculator_picklesafe(calculator)
try:
calculator_default = construct_calculator_picklesafe(calculator)
calculator_failure_message = None
except Exception as exc:
# if calculator constructor failed, it may still be fine if every atoms object has
# enough info to construct its own calculator, but we won't know until later
calculator_failure_message = str(exc)
calculator_default = None

if output_prefix == '_auto_':
output_prefix = calculator.__class__.__name__ + '_'

at_out = []
for at in atoms_to_list(atoms):
at.calc = calculator
calculator_use = calculator_default
if ("WFL_CALCULATOR_INITIALIZER" in at.info or
"WFL_CALCULATOR_ARGS" in at.info or
"WFL_CALCULATOR_KWARGS" in at.info):
# create per-config Calculator
try:
initializer_use = at.info.get("WFL_CALCULATOR_INITIALIZER", calculator[0])
args_use = at.info.get("WFL_CALCULATOR_ARGS", calculator[1])
kwargs_use = calculator[2].copy()
kwargs_use.update(at.info.get("WFL_CALCULATOR_KWARGS", {}))
calculator_use = construct_calculator_picklesafe((initializer_use, args_use, kwargs_use))
except Exception as exc:
raise TypeError("calculators.generic.calculate got WFL_CALCULATOR_INITIALIZER, _ARGS, or _KWARGS "
f"but constructor failed, most likely because calculator wasn't a tuple (TypeError) "
"or original tuple had invalid element that wasn't overridden by `Atoms.info` entry. "
f"Constructor exception was '{exc}'")

if calculator_use is None:
raise ValueError(f"Failed to construct calculator, original attempt's exception was '{calculator_failure_message}'")
at.calc = calculator_use

calculation_succeeded = False
try:
# explicitly pass system_changes=all_changes because some calculators, e.g. ace.ACECalculator,
Expand Down