Skip to content

Commit a401cd2

Browse files
Bug fixes in force field flows + housekeeping (#1286)
* minor bug fixes + pytest xdist for on-runner parallelism * bump precommit and remove deprecated pragma * ensure aims test is independent of run order
1 parent 84b838c commit a401cd2

File tree

40 files changed

+544
-453
lines changed

40 files changed

+544
-453
lines changed

.github/workflows/testing.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
# However this `splitting-algorithm` means that tests cannot depend sensitively on the order they're executed in.
8888
run: |
8989
micromamba activate a2
90-
pytest --splits 3 --group ${{ matrix.split }} --durations-path tests/.pytest-split-durations --splitting-algorithm least_duration --ignore=tests/ase --ignore=tests/openff_md --ignore=tests/openmm_md --cov=atomate2 --cov-report=xml
90+
pytest -n auto --splits 3 --group ${{ matrix.split }} --durations-path tests/.pytest-split-durations --splitting-algorithm least_duration --ignore=tests/ase --ignore=tests/openff_md --ignore=tests/openmm_md --cov=atomate2 --cov-report=xml
9191
9292
9393
- uses: codecov/codecov-action@v1
@@ -151,7 +151,7 @@ jobs:
151151

152152
run: |
153153
micromamba activate a2
154-
pytest tests/{openff_md,openmm_md}
154+
pytest -n auto tests/{openff_md,openmm_md}
155155
156156
test-notebooks-and-ase:
157157
# prevent this action from running on forks
@@ -210,14 +210,14 @@ jobs:
210210
MP_API_KEY: ${{ secrets.MP_API_KEY }}
211211
run: |
212212
micromamba activate a2
213-
pytest --nbmake ./tutorials --ignore=./tutorials/openmm_tutorial.ipynb --ignore=./tutorials/force_fields
213+
pytest -n auto --nbmake ./tutorials --ignore=./tutorials/openmm_tutorial.ipynb --ignore=./tutorials/force_fields
214214
215215
- name: Test ASE
216216
env:
217217
MP_API_KEY: ${{ secrets.MP_API_KEY }}
218218
run: |
219219
micromamba activate a2
220-
pytest --splits 1 --group 1 --cov=atomate2 --cov-report=xml tests/ase
220+
pytest -n auto --splits 1 --group 1 --cov=atomate2 --cov-report=xml tests/ase
221221
222222
- uses: codecov/codecov-action@v1
223223
if: matrix.python-version == '3.10' && github.repository == 'materialsproject/atomate2'
@@ -288,7 +288,7 @@ jobs:
288288
# However this `splitting-algorithm` means that tests cannot depend sensitively on the order they're executed in.
289289
run: |
290290
micromamba activate a2
291-
pytest --nbmake ./tutorials/force_fields
291+
pytest -n auto --nbmake ./tutorials/force_fields
292292
293293
294294
- uses: codecov/codecov-action@v1

.pre-commit-config.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ default_language_version:
33
exclude: ^(.github/|tests/test_data/abinit/)
44
repos:
55
- repo: https://github.com/charliermarsh/ruff-pre-commit
6-
rev: v0.9.3
6+
rev: v0.12.10
77
hooks:
88
- id: ruff
99
args: [--fix]
1010
exclude: tutorials/grueneisen_workflow.ipynb
1111
- id: ruff-format
1212
- repo: https://github.com/pre-commit/pre-commit-hooks
13-
rev: v5.0.0
13+
rev: v6.0.0
1414
hooks:
1515
- id: check-yaml
16-
- id: fix-encoding-pragma
17-
args: [--remove]
1816
- id: end-of-file-fixer
1917
- id: trailing-whitespace
18+
- repo: https://github.com/asottile/pyupgrade
19+
rev: v3.20.0
20+
hooks:
21+
- id: pyupgrade
2022
- repo: https://github.com/asottile/blacken-docs
2123
rev: 1.19.1
2224
hooks:
@@ -31,15 +33,15 @@ repos:
3133
- id: rst-directive-colons
3234
- id: rst-inline-touching-normal
3335
- repo: https://github.com/pre-commit/mirrors-mypy
34-
rev: v1.14.1
36+
rev: v1.17.1
3537
hooks:
3638
- id: mypy
3739
files: ^src/
3840
additional_dependencies:
3941
- tokenize-rt==4.1.0
4042
- types-paramiko
4143
- repo: https://github.com/codespell-project/codespell
42-
rev: v2.4.0
44+
rev: v2.4.1
4345
hooks:
4446
- id: codespell
4547
stages: [pre-commit, commit-msg]

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ tests = [
9191
"pytest-cov==6.2.1",
9292
"pytest-mock==3.14.1",
9393
"pytest-split==0.10.0",
94+
"pytest-xdist==3.8.0",
9495
"pytest==8.4.1",
9596
]
9697
strict = [
@@ -105,7 +106,7 @@ strict = [
105106
"jobflow==0.2.0",
106107
"lobsterpy==0.5.7",
107108
"monty==2025.3.3",
108-
"mp-api==0.45.7",
109+
"mp-api==0.45.8",
109110
"numpy",
110111
"phonopy==2.30.1",
111112
"pydantic-settings==2.10.1",
@@ -130,7 +131,7 @@ strict-forcefields = [
130131
"mace-torch==0.3.14",
131132
"matgl==1.3.0",
132133
"quippy-ase==0.9.14; python_version < '3.12'",
133-
"sevenn==0.10.3",
134+
"sevenn==0.10.4",
134135
"torch==2.2.0",
135136
"torchdata==0.7.1", # TODO: remove when issue fixed
136137
]
@@ -209,6 +210,7 @@ ignore = [
209210
"ISC001",
210211
"PD011", # pandas-use-of-dot-values
211212
"PERF203", # try-except-in-loop
213+
"PLC0415", # imports not at top level are OK
212214
"PLR0911", # too many returns
213215
"PLR0912", # too many branches
214216
"PLR0913", # too many arguments

src/atomate2/abinit/schemas/calculation.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import os
77
from datetime import datetime, timezone
88
from pathlib import Path
9-
from typing import Optional, Union
109

1110
from abipy.electrons.gsr import GsrFile
1211
from abipy.flowtk import events
@@ -74,31 +73,31 @@ class CalculationOutput(BaseModel):
7473
None, description="The final DFT energy per atom for the calculation"
7574
)
7675

77-
structure: Union[Structure] = Field(
76+
structure: Structure = Field(
7877
None, description="The final structure from the calculation"
7978
)
8079

8180
efermi: float = Field(
8281
None, description="The Fermi level from the calculation in eV"
8382
)
8483

85-
forces: Optional[list[Vector3D]] = Field(
84+
forces: list[Vector3D] | None = Field(
8685
None, description="Forces acting on each atom"
8786
)
88-
stress: Optional[Matrix3D] = Field(None, description="The stress on the cell")
89-
is_metal: Optional[bool] = Field(None, description="Whether the system is metallic")
90-
bandgap: Optional[float] = Field(
87+
stress: Matrix3D | None = Field(None, description="The stress on the cell")
88+
is_metal: bool | None = Field(None, description="Whether the system is metallic")
89+
bandgap: float | None = Field(
9190
None, description="The band gap from the calculation in eV"
9291
)
93-
direct_bandgap: Optional[float] = Field(
92+
direct_bandgap: float | None = Field(
9493
None, description="The direct band gap from the calculation in eV"
9594
)
96-
cbm: Optional[float] = Field(
95+
cbm: float | None = Field(
9796
None,
9897
description="The conduction band minimum, or LUMO for molecules, in eV "
9998
"(if system is not metallic)",
10099
)
101-
vbm: Optional[float] = Field(
100+
vbm: float | None = Field(
102101
None,
103102
description="The valence band maximum, or HOMO for molecules, in eV "
104103
"(if system is not metallic)",
@@ -194,7 +193,7 @@ class Calculation(BaseModel):
194193
event_report: events.EventReport = Field(
195194
None, description="Event report of this abinit job."
196195
)
197-
output_file_paths: Optional[dict[str, str]] = Field(
196+
output_file_paths: dict[str, str] | None = Field(
198197
None,
199198
description="Paths (relative to dir_name) of the Abinit output files "
200199
"associated with this calculation",
@@ -257,7 +256,7 @@ def from_abinit_files(
257256
if report.run_completed:
258257
has_abinit_completed = TaskState.SUCCESS
259258

260-
except (ValueError, RuntimeError, Exception) as exc:
259+
except (ValueError, RuntimeError, Exception) as exc: # noqa: BLE001
261260
msg = f"{cls} exception while parsing event_report:\n{exc}"
262261
logger.critical(msg)
263262

src/atomate2/abinit/schemas/task.py

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
from collections.abc import Sequence
77
from pathlib import Path
8-
from typing import Any, Optional, Union
8+
from typing import Any
99

1010
from abipy.abio.inputs import AbinitInput
1111
from abipy.flowtk import events
@@ -33,7 +33,7 @@ class InputDoc(BaseModel):
3333
The final pymatgen Structure of the final system
3434
"""
3535

36-
structure: Union[Structure] = Field(None, description="The input structure object")
36+
structure: Structure = Field(None, description="The input structure object")
3737
abinit_input: AbinitInput = Field(
3838
None, description="AbinitInput used to perform calculation."
3939
)
@@ -88,8 +88,8 @@ class OutputDoc(BaseModel):
8888
Stress on the unit cell from the last calculation
8989
"""
9090

91-
structure: Union[Structure] = Field(None, description="The output structure object")
92-
trajectory: Optional[Sequence[Union[Structure]]] = Field(
91+
structure: Structure = Field(None, description="The output structure object")
92+
trajectory: Sequence[Structure] | None = Field(
9393
None, description="The trajectory of output structures"
9494
)
9595
energy: float = Field(
@@ -98,15 +98,15 @@ class OutputDoc(BaseModel):
9898
energy_per_atom: float = Field(
9999
None, description="The final DFT energy per atom for the last calculation"
100100
)
101-
bandgap: Optional[float] = Field(
101+
bandgap: float | None = Field(
102102
None, description="The DFT bandgap for the last calculation"
103103
)
104-
cbm: Optional[float] = Field(None, description="CBM for this calculation")
105-
vbm: Optional[float] = Field(None, description="VBM for this calculation")
106-
forces: Optional[list[Vector3D]] = Field(
104+
cbm: float | None = Field(None, description="CBM for this calculation")
105+
vbm: float | None = Field(None, description="VBM for this calculation")
106+
forces: list[Vector3D] | None = Field(
107107
None, description="Forces on atoms from the last calculation"
108108
)
109-
stress: Optional[Matrix3D] = Field(
109+
stress: Matrix3D | None = Field(
110110
None, description="Stress on the unit cell from the last calculation"
111111
)
112112

@@ -179,49 +179,45 @@ class AbinitTaskDoc(StructureMetadata):
179179
Additional json loaded from the calculation directory
180180
"""
181181

182-
dir_name: Optional[str] = Field(
183-
None, description="The directory for this Abinit task"
184-
)
185-
last_updated: Optional[str] = Field(
182+
dir_name: str | None = Field(None, description="The directory for this Abinit task")
183+
last_updated: str | None = Field(
186184
default_factory=datetime_str,
187185
description="Timestamp for when this task document was last updated",
188186
)
189-
completed_at: Optional[str] = Field(
187+
completed_at: str | None = Field(
190188
None, description="Timestamp for when this task was completed"
191189
)
192-
input: Optional[InputDoc] = Field(
190+
input: InputDoc | None = Field(
193191
None, description="The input to the first calculation"
194192
)
195-
output: Optional[OutputDoc] = Field(
193+
output: OutputDoc | None = Field(
196194
None, description="The output of the final calculation"
197195
)
198-
structure: Union[Structure] = Field(
199-
None, description="Final output atoms from the task"
200-
)
201-
state: Optional[TaskState] = Field(None, description="State of this task")
202-
event_report: Optional[events.EventReport] = Field(
196+
structure: Structure = Field(None, description="Final output atoms from the task")
197+
state: TaskState | None = Field(None, description="State of this task")
198+
event_report: events.EventReport | None = Field(
203199
None, description="Event report of this abinit job."
204200
)
205-
included_objects: Optional[list[AbinitObject]] = Field(
201+
included_objects: list[AbinitObject] | None = Field(
206202
None, description="List of Abinit objects included with this task document"
207203
)
208-
abinit_objects: Optional[dict[AbinitObject, Any]] = Field(
204+
abinit_objects: dict[AbinitObject, Any] | None = Field(
209205
None, description="Abinit objects associated with this task"
210206
)
211-
task_label: Optional[str] = Field(None, description="A description of the task")
212-
tags: Optional[list[str]] = Field(
207+
task_label: str | None = Field(None, description="A description of the task")
208+
tags: list[str] | None = Field(
213209
None, description="Metadata tags for this task document"
214210
)
215-
author: Optional[str] = Field(
211+
author: str | None = Field(
216212
None, description="Author extracted from transformations"
217213
)
218-
icsd_id: Optional[str] = Field(
214+
icsd_id: str | None = Field(
219215
None, description="International crystal structure database id of the structure"
220216
)
221-
calcs_reversed: Optional[list[Calculation]] = Field(
217+
calcs_reversed: list[Calculation] | None = Field(
222218
None, description="The inputs and outputs for all Abinit runs in this task."
223219
)
224-
transformations: Optional[dict[str, Any]] = Field(
220+
transformations: dict[str, Any] | None = Field(
225221
None,
226222
description="Information on the structural transformations, parsed from a "
227223
"transformations.json file",
@@ -231,7 +227,7 @@ class AbinitTaskDoc(StructureMetadata):
231227
description="Information on the custodian settings used to run this "
232228
"calculation, parsed from a custodian.json file",
233229
)
234-
additional_json: Optional[dict[str, Any]] = Field(
230+
additional_json: dict[str, Any] | None = Field(
235231
None, description="Additional json loaded from the calculation directory"
236232
)
237233

src/atomate2/abinit/utils/common.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ def get_final_structure(dir_name: Path | str) -> Structure:
352352
try:
353353
gsr_file = GsrFile(gsr_path)
354354
except Exception:
355-
logging.exception("Exception occurred")
355+
logging.exception("Exception occurred") # noqa: LOG015
356356
else:
357357
return gsr_file.structure
358358

@@ -362,7 +362,7 @@ def get_final_structure(dir_name: Path | str) -> Structure:
362362
try:
363363
ddb_file = DdbFile(ddb_path)
364364
except Exception:
365-
logging.exception("Exception occurred")
365+
logging.exception("Exception occurred") # noqa: LOG015
366366
else:
367367
return ddb_file.structure
368368

@@ -371,7 +371,7 @@ def get_final_structure(dir_name: Path | str) -> Structure:
371371
try:
372372
ab_out = AbinitOutputFile.from_file(out_path.path)
373373
except Exception:
374-
logging.exception("Exception occurred")
374+
logging.exception("Exception occurred") # noqa: LOG015
375375
else:
376376
return ab_out.final_structure
377377

@@ -425,7 +425,7 @@ def get_event_report(ofile: File, mpiabort_file: File) -> EventReport | None:
425425
report.append(last_abort_event)
426426
else:
427427
report.append(last_abort_event)
428-
except (ValueError, RuntimeError, Exception) as exc:
428+
except (ValueError, RuntimeError, Exception) as exc: # noqa: BLE001
429429
# Return a report with an error entry with info on the exception.
430430
logger.critical(f"{ofile}: Exception while parsing ABINIT events:\n {exc!s}")
431431
return parser.report_exception(ofile.path, exc)

0 commit comments

Comments
 (0)