Skip to content

Commit eed206e

Browse files
MPMorph bug fixes
* correct unassigned variable and incorrectly-named energy kwarg in the MPMorph flow
1 parent 9bbbb9e commit eed206e

File tree

2 files changed

+68
-20
lines changed

2 files changed

+68
-20
lines changed

src/atomate2/common/flows/mpmorph.py

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -112,29 +112,24 @@ def make(
112112
*self.initial_strain, self.postprocessor.min_data_points
113113
)
114114
working_outputs = {
115-
"relax": {
116-
key: [] for key in ("energies", "volume", "stress", "pressure")
117-
}
115+
"relax": {key: [] for key in ("energy", "volume", "stress", "pressure")}
118116
}
119117

120118
else:
121119
# Fit EOS to running list of energies and volumes
122120
self.postprocessor.fit(working_outputs)
123121
working_outputs = dict(self.postprocessor.results)
124122
flow_output = {"working_outputs": working_outputs.copy(), "structure": None}
125-
for k in ("pressure", "energy"):
123+
for k in ("pressure",):
126124
working_outputs["relax"].pop(k, None)
127125

128126
# Stop flow here if EOS cannot be fit
129127
if (v0 := working_outputs.get("V0")) is None:
130128
return Response(output=flow_output, stop_children=True)
131129

132130
# Check if equilibrium volume is in range of attempted volumes
133-
v0_in_range = (
134-
(vmin := working_outputs.get("Vmin"))
135-
<= v0
136-
<= (vmax := working_outputs.get("Vmax"))
137-
)
131+
vmin = working_outputs.get("Vmin")
132+
vmax = working_outputs.get("Vmax")
138133

139134
# Check if maximum number of refinement NVT runs is set,
140135
# and if so, if that limit has been reached
@@ -143,7 +138,7 @@ def make(
143138
)
144139

145140
# Successful fit: return structure at estimated equilibrium volume
146-
if v0_in_range or max_attempts_reached:
141+
if (vmin <= v0 <= vmax) or max_attempts_reached:
147142
flow_output["structure"] = structure.copy()
148143
flow_output["structure"].scale_lattice(v0) # type: ignore[attr-defined]
149144
return flow_output
@@ -162,19 +157,19 @@ def make(
162157
eos_jobs = []
163158
for index in range(len(deformation_matrices)):
164159
md_job = self.md_maker.make(
165-
structure=deformed_structures[index].final_structure,
160+
deformed_structures[index].final_structure,
166161
prev_dir=None,
167162
)
168163
relaxed_vol = len(working_outputs["relax"]["volume"])
169164
md_job.name = f"{self.name} {md_job.name} {relaxed_vol + 1}"
170165

171-
working_outputs["relax"]["energies"].append(md_job.output.output.energy)
166+
working_outputs["relax"]["energy"].append(md_job.output.output.energy)
172167
working_outputs["relax"]["volume"].append(md_job.output.structure.volume)
173168
working_outputs["relax"]["stress"].append(md_job.output.output.stress)
174169
eos_jobs.append(md_job)
175170

176171
recursive = self.make(
177-
structure=structure,
172+
structure,
178173
prev_dir=None,
179174
working_outputs=working_outputs,
180175
)
@@ -262,14 +257,12 @@ def make(
262257
structure = convergence_flow.output["structure"]
263258

264259
self.production_md_maker.name = self.name + " production run"
265-
production_run = self.production_md_maker.make(
266-
structure=structure, prev_dir=prev_dir
267-
)
260+
production_run = self.production_md_maker.make(structure, prev_dir=prev_dir)
268261
flow_jobs.append(production_run)
269262

270263
if self.quench_maker:
271264
quench_flow = self.quench_maker.make(
272-
structure=production_run.output.structure,
265+
production_run.output.structure,
273266
prev_dir=production_run.output.dir_name,
274267
)
275268
flow_jobs += [quench_flow]
@@ -469,7 +462,7 @@ def make(self, structure: Structure, prev_dir: str | Path | None = None) -> Flow
469462
)
470463
if self.descent_method == "stepwise":
471464
md_job = self.call_md_maker(
472-
structure=structure,
465+
structure,
473466
temp=temp,
474467
prev_dir=prev_dir,
475468
)
@@ -478,13 +471,13 @@ def make(self, structure: Structure, prev_dir: str | Path | None = None) -> Flow
478471
self.descent_method == "linear with hold"
479472
): # TODO: Work in Progress; needs testing
480473
md_job_linear = self.call_md_maker(
481-
structure=structure,
474+
structure,
482475
temp=[temp, temp - self.quench_temperature_step], # type: ignore[arg-type]
483476
prev_dir=prev_dir,
484477
)
485478

486479
md_job = self.call_md_maker(
487-
structure=md_job_linear.output.structure,
480+
md_job_linear.output.structure,
488481
temp=temp - self.quench_temperature_step,
489482
prev_dir=md_job_linear.output.dir_name,
490483
)

tests/ase/flows/test_mpmorph.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""Test MPMoprh flow using Lennard-Jones."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
import pytest
8+
from jobflow import run_locally
9+
10+
from atomate2.ase.jobs import LennardJonesStaticMaker
11+
from atomate2.common.flows.mpmorph import EquilibriumVolumeMaker
12+
13+
if TYPE_CHECKING:
14+
from pymatgen.core import Structure
15+
16+
17+
@pytest.mark.parametrize("vol_scale", [1.25, 0.75])
18+
def test_vol_scale(
19+
lj_fcc_ne_pars: dict[str, float], fcc_ne_structure: Structure, vol_scale: float
20+
) -> None:
21+
# Perform single points here instead of MD for
22+
# (1) time cost and (2) reliability of output
23+
# This is a coarse test to ensure that the volume up and down scaling can occur
24+
25+
test_structure = fcc_ne_structure.to_conventional()
26+
test_structure = test_structure.scale_lattice(vol_scale * test_structure.volume)
27+
test_structure = test_structure * (2, 2, 2)
28+
init_vol_per_atom = test_structure.volume
29+
30+
init_strain = 0.05
31+
vscale = ((1 - init_strain) ** 3, (1 + init_strain) ** 3)
32+
flow = EquilibriumVolumeMaker(
33+
md_maker=LennardJonesStaticMaker(
34+
calculator_kwargs=lj_fcc_ne_pars,
35+
),
36+
min_strain=0.05,
37+
initial_strain=0.05,
38+
).make(test_structure)
39+
40+
resp = run_locally(flow, ensure_success=True)
41+
output = None
42+
for _output in resp.values():
43+
if isinstance(_output[1].output, dict) and (
44+
output := _output[1].output.get("working_outputs")
45+
):
46+
break
47+
48+
# ensure that at least one refinement on the volume range was undertaken
49+
assert len(output["relax"]["volume"]) > 3
50+
51+
final_v_ratio = output["V0"] / init_vol_per_atom
52+
if vol_scale > 1.0:
53+
assert final_v_ratio <= vscale[1]
54+
elif vol_scale < 1.0:
55+
assert vscale[0] <= final_v_ratio

0 commit comments

Comments
 (0)