Skip to content

Commit 8a2f937

Browse files
try to fix mypy ci
1 parent b7b8e81 commit 8a2f937

File tree

7 files changed

+107
-111
lines changed

7 files changed

+107
-111
lines changed

.pre-commit-config.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,13 @@ repos:
5252
args:
5353
- --namespace-packages
5454
- --explicit-package-bases
55-
additional_dependencies: ['types-requests','pydantic>=2.0.1']
55+
additional_dependencies: ['types-requests','pydantic>=2.10.0']
56+
57+
- repo: https://github.com/kynan/nbstripout
58+
rev: 0.8.1
59+
hooks:
60+
- id: nbstripout
61+
args:
62+
- --drop-empty-cells
63+
- --strip-init-cells
64+
- --extra-keys=metadata.kernelspec

examples/using_validation_docs.ipynb

Lines changed: 74 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,43 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": 1,
6-
"id": "b116c8b3-e927-401b-aed8-994fe5279b54",
5+
"execution_count": null,
6+
"id": "0",
77
"metadata": {},
88
"outputs": [],
99
"source": [
1010
"from __future__ import annotations\n",
1111
"\n",
12-
"from emmet.core.tasks import TaskDoc\n",
12+
"from monty.os.path import zpath\n",
1313
"from monty.serialization import loadfn\n",
1414
"import os\n",
15+
"from pathlib import Path\n",
1516
"\n",
16-
"from pymatgen.io.validation import ValidationDoc\n",
17-
"from pymatgen.io.validation.check_potcar import CheckPotcar\n",
17+
"from pymatgen.io.validation.validation import VaspValidator\n",
1818
"\n",
1919
"from pymatgen.io.vasp import PotcarSingle, Potcar"
2020
]
2121
},
2222
{
23-
"cell_type": "code",
24-
"execution_count": 2,
25-
"id": "80064515-1e98-43da-b075-5a4c41ede437",
23+
"cell_type": "markdown",
24+
"id": "1",
2625
"metadata": {},
27-
"outputs": [],
2826
"source": [
29-
"\"\"\"\n",
3027
"For copyright reasons, the POTCAR for these calculations cannot be distributed with this file, but its summary stats can.\n",
31-
"If you have the POTCAR resources set up in pymatgen, you can regenerate the POTCARs used here by enabling `regen_potcars`\n",
32-
"\"\"\"\n",
3328
"\n",
29+
"If you have the POTCAR resources set up in pymatgen, you can regenerate the POTCARs used here by enabling `regen_potcars`"
30+
]
31+
},
32+
{
33+
"cell_type": "code",
34+
"execution_count": null,
35+
"id": "2",
36+
"metadata": {},
37+
"outputs": [],
38+
"source": [
3439
"regen_potcars = True\n",
3540
"\n",
3641
"def get_potcar_from_spec(potcar_spec : dict) -> Potcar | None:\n",
37-
" potcar_checker = CheckPotcar()\n",
3842
" \n",
3943
" for functional in PotcarSingle._potcar_summary_stats:\n",
4044
"\n",
@@ -47,72 +51,83 @@
4751
" \n",
4852
" for stats in PotcarSingle._potcar_summary_stats[functional].get(titel_no_spc,[]):\n",
4953
" \n",
50-
" if potcar_checker.compare_potcar_stats(spec[\"summary_stats\"], stats):\n",
54+
" if PotcarSingle.compare_potcar_stats(spec[\"summary_stats\"], stats):\n",
5155
" potcar.append(PotcarSingle.from_symbol_and_functional(symbol=symbol, functional=functional))\n",
5256
" matched[ispec] = True\n",
5357
" break\n",
5458
" \n",
5559
" if all(matched):\n",
5660
" return potcar\n",
5761
" \n",
58-
"def check_calc(calc_dir : str) -> ValidationDoc:\n",
62+
"def check_calc(calc_dir : str | Path) -> VaspValidator:\n",
63+
"\n",
64+
" calc_dir = Path(calc_dir)\n",
5965
" potcar_filename = None\n",
6066
" if regen_potcars:\n",
61-
" potcar = get_potcar_from_spec(loadfn(os.path.join(calc_dir,\"POTCAR.spec.gz\")))\n",
67+
" potcar = get_potcar_from_spec(loadfn(calc_dir / \"POTCAR.spec.gz\"))\n",
6268
" if potcar:\n",
63-
" potcar_filename = os.path.join(calc_dir,\"POTCAR.gz\")\n",
69+
" potcar_filename = calc_dir / \"POTCAR.gz\"\n",
6470
" potcar.write_file(potcar_filename)\n",
6571
" \n",
66-
" valid_doc = ValidationDoc.from_directory(calc_dir, check_potcar=(regen_potcars and potcar))\n",
72+
" vasp_files = {\n",
73+
" k.lower().split(\".\")[0] : zpath(calc_dir / k) for k in (\n",
74+
" \"INCAR\",\"KPOINTS\",\"POSCAR\",\"POTCAR\",\"OUTCAR\", \"vasprun.xml\"\n",
75+
" )\n",
76+
" }\n",
6777
" \n",
78+
" valid_doc = VaspValidator.from_vasp_input(\n",
79+
" vasp_file_paths={\n",
80+
" k : v for k,v in vasp_files.items() if Path(v).exists()\n",
81+
" },\n",
82+
" check_potcar=(regen_potcars and potcar)\n",
83+
" )\n",
84+
"\n",
6885
" if potcar_filename and potcar:\n",
6986
" os.remove(potcar_filename)\n",
7087
" \n",
7188
" return valid_doc\n",
7289
" "
7390
]
7491
},
92+
{
93+
"cell_type": "markdown",
94+
"id": "3",
95+
"metadata": {},
96+
"source": [
97+
"An example of an MP-compatible r2SCAN static calculation for GaAs is located in the `MP_compliant` directory."
98+
]
99+
},
75100
{
76101
"cell_type": "code",
77-
"execution_count": 3,
78-
"id": "0f660f54-ca8a-466c-b382-2f0fac46d8bf",
102+
"execution_count": null,
103+
"id": "4",
79104
"metadata": {},
80-
"outputs": [
81-
{
82-
"name": "stdout",
83-
"output_type": "stream",
84-
"text": [
85-
"True\n"
86-
]
87-
}
88-
],
105+
"outputs": [],
89106
"source": [
90-
"\"\"\"\n",
91-
"An example of an MP-compatible r2SCAN static calculation for GaAs is located in the `MP_compliant` directory.\n",
92-
"\"\"\"\n",
93107
"mp_compliant_doc = check_calc(\"MP_compliant\")\n",
94-
"print(mp_compliant_doc.valid)"
108+
"print(mp_compliant_doc.is_valid)"
109+
]
110+
},
111+
{
112+
"cell_type": "markdown",
113+
"id": "5",
114+
"metadata": {},
115+
"source": [
116+
"We also include `TaskDoc` objects generated with `atomate2`, the workflow software currently used by the Materials Project (MP) for high-throughput calculations. A `TaskDoc` is also the document schema for the MP `task` collection.\n",
117+
"\n",
118+
"If you have `emmet-core` (this is the software used to build Materials Project data) or `atomate2` installed, you can load a `TaskDoc` like this:"
95119
]
96120
},
97121
{
98122
"cell_type": "code",
99-
"execution_count": 4,
100-
"id": "25b85de2",
123+
"execution_count": null,
124+
"id": "6",
101125
"metadata": {},
102-
"outputs": [
103-
{
104-
"name": "stdout",
105-
"output_type": "stream",
106-
"text": [
107-
"True\n"
108-
]
109-
}
110-
],
126+
"outputs": [],
111127
"source": [
112-
"\"\"\"\n",
113-
"TaskDocs for these calculations (generated with atomate2) are also saved in these directories.\n",
114-
"You can load in the TaskDocs like so:\n",
115-
"\"\"\"\n",
128+
"from emmet.core.tasks import TaskDoc\n",
129+
"from pymatgen.io.validation.emmet_validation import ValidationDoc\n",
130+
"\n",
116131
"compliant_task_doc = TaskDoc(\n",
117132
" **loadfn(os.path.join(\"MP_compliant\",\"MP_compatible_GaAs_r2SCAN_static.json.gz\"))\n",
118133
")\n",
@@ -121,59 +136,31 @@
121136
]
122137
},
123138
{
124-
"cell_type": "code",
125-
"execution_count": 5,
126-
"id": "c919fedd-38ef-4cf7-a2ed-54544eec8d82",
139+
"cell_type": "markdown",
140+
"id": "7",
127141
"metadata": {},
128-
"outputs": [
129-
{
130-
"name": "stdout",
131-
"output_type": "stream",
132-
"text": [
133-
"False\n",
134-
"INPUT SETTINGS --> KPOINTS or KSPACING: 64 kpoints were used, but it should have been at least 194.\n",
135-
"INPUT SETTINGS --> ENAUG: is 900.0, but should be >= 1360.\n",
136-
"INPUT SETTINGS --> ENCUT: is 450.0, but should be >= 680.\n",
137-
"False\n",
138-
"True\n"
139-
]
140-
}
141-
],
142142
"source": [
143-
"\"\"\"\n",
144-
"An example of an MP incompatible r2SCAN static calculation for GaAs is located in the `MP_non_compliant` directory.\n",
143+
"An example of an MP incompatible r<sup>2</sup>SCAN static calculation for GaAs is located in the `MP_non_compliant` directory.\n",
145144
"\n",
146145
"This calculation uses a lower ENCUT, ENAUG, and k-point density (larger KSPACING) than is permitted by the appropriate input set, `pymatgen.io.vasp.sets.MPScanStaticSet`.\n",
147-
"These reasons are reflected transparently in the output reasons.\n",
148-
"\"\"\"\n",
149-
"mp_non_compliant_doc = check_calc(\"MP_non_compliant\")\n",
150-
"print(mp_non_compliant_doc.valid)\n",
151-
"for reason in mp_non_compliant_doc.reasons:\n",
152-
" print(reason)\n",
153-
"\n",
154-
"non_compliant_task_doc = TaskDoc(\n",
155-
" **loadfn(os.path.join(\"MP_non_compliant\",\"MP_incompatible_GaAs_r2SCAN_static.json.gz\"))\n",
156-
")\n",
157-
"mp_non_compliant_doc_from_taskdoc = ValidationDoc.from_task_doc(non_compliant_task_doc)\n",
158-
"print(mp_non_compliant_doc_from_taskdoc.valid)\n",
159-
"print(mp_non_compliant_doc_from_taskdoc.reasons == mp_non_compliant_doc_from_taskdoc.reasons)"
146+
"These reasons are reflected transparently in the output reasons."
160147
]
161148
},
162149
{
163150
"cell_type": "code",
164151
"execution_count": null,
165-
"id": "128e49d1",
152+
"id": "8",
166153
"metadata": {},
167154
"outputs": [],
168-
"source": []
155+
"source": [
156+
"mp_non_compliant_doc = check_calc(\"MP_non_compliant\")\n",
157+
"print(mp_non_compliant_doc.is_valid)\n",
158+
"for reason in mp_non_compliant_doc.reasons:\n",
159+
" print(reason)"
160+
]
169161
}
170162
],
171163
"metadata": {
172-
"kernelspec": {
173-
"display_name": "Python 3 (ipykernel)",
174-
"language": "python",
175-
"name": "python3"
176-
},
177164
"language_info": {
178165
"codemirror_mode": {
179166
"name": "ipython",

pymatgen/io/validation/common.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88
from pathlib import Path
99
from pydantic import BaseModel, Field, model_serializer, PrivateAttr
10-
from typing import TYPE_CHECKING, Any
10+
from typing import TYPE_CHECKING, Any, Optional
1111

1212
from pymatgen.core import Structure
1313
from pymatgen.io.vasp import Incar, Kpoints, Poscar, Potcar, Outcar, Vasprun
@@ -55,8 +55,8 @@ class _PotcarSummaryStatsNames(BaseModel):
5555
header: _PotcarSummaryStatsNames = Field(description="The keywords in the POTCAR header.")
5656
data: _PotcarSummaryStatsNames = Field(description="The keywords in the POTCAR body.")
5757

58-
keywords: _PotcarSummaryStatsKeywords | None = None
59-
stats: _PotcarSummaryStatsStats | None = None
58+
keywords: Optional[_PotcarSummaryStatsKeywords] = None
59+
stats: Optional[_PotcarSummaryStatsStats] = None
6060
titel: str
6161
lexch: str
6262

@@ -73,8 +73,8 @@ def from_file(cls, potcar_path: os.PathLike | Potcar) -> list[Self]:
7373
class LightOutcar(BaseModel):
7474
"""Schematic of pymatgen's Outcar."""
7575

76-
drift: list[list[float]] | None = Field(None, description="The drift forces.")
77-
magnetization: list[dict[str, float]] | None = Field(
76+
drift: Optional[list[list[float]]] = Field(None, description="The drift forces.")
77+
magnetization: Optional[list[dict[str, float]]] = Field(
7878
None, description="The on-site magnetic moments, possibly with orbital resolution."
7979
)
8080

@@ -89,7 +89,7 @@ class LightVasprun(BaseModel):
8989
kpoints: Kpoints = Field(description="The actual k-points used in the calculation.")
9090
parameters: dict[str, Any] = Field(description="The default-padded input parameters interpreted by VASP.")
9191
bandgap: float = Field(description="The bandgap - note that this field is derived from the Vasprun object.")
92-
potcar_symbols: list[str] | None = Field(
92+
potcar_symbols: Optional[list[str]] = Field(
9393
None,
9494
description="Optional: if a POTCAR is unavailable, this is used to determine the functional used in the calculation.",
9595
)
@@ -107,10 +107,10 @@ class VaspInputSafe(BaseModel):
107107

108108
incar: Incar = Field(description="The INCAR used in the calculation.")
109109
structure: Structure = Field(description="The structure associated with the calculation.")
110-
kpoints: Kpoints | None = Field(None, description="The optional KPOINTS or IBZKPT file used in the calculation.")
111-
potcar: list[PotcarSummaryStats] | None = Field(None, description="The optional POTCAR used in the calculation.")
112-
potcar_functional: str | None = Field(None, description="The pymatgen-labelled POTCAR library release.")
113-
_pmg_vis: VaspInputSet | None = PrivateAttr(None)
110+
kpoints: Optional[Kpoints] = Field(None, description="The optional KPOINTS or IBZKPT file used in the calculation.")
111+
potcar: Optional[list[PotcarSummaryStats]] = Field(None, description="The optional POTCAR used in the calculation.")
112+
potcar_functional: Optional[str] = Field(None, description="The pymatgen-labelled POTCAR library release.")
113+
_pmg_vis: Optional[VaspInputSet] = PrivateAttr(None)
114114

115115
@model_serializer
116116
def deserialize_objects(self) -> dict[str, Any]:
@@ -155,8 +155,8 @@ class VaspFiles(BaseModel):
155155
"""Define required and optional files for validation."""
156156

157157
user_input: VaspInputSafe = Field(description="The VASP input set used in the calculation.")
158-
outcar: LightOutcar | None = None
159-
vasprun: LightVasprun | None = None
158+
outcar: Optional[LightOutcar] = None
159+
vasprun: Optional[LightVasprun] = None
160160

161161
@property
162162
def actual_kpoints(self) -> Kpoints | None:

pymatgen/io/validation/vasp_defaults.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Define VASP defaults and input categories to check."""
22

33
from __future__ import annotations
4-
from typing import Any, Literal
4+
from typing import Any, Literal, Optional
55
import math
66
from pathlib import Path
77
from pydantic import BaseModel, Field, field_validator
@@ -70,16 +70,16 @@ class VaspParam(BaseModel):
7070
tag: str = Field(
7171
description="the general category of input the tag belongs to. Used only to properly update INCAR fields in the same way VASP does."
7272
)
73-
operation: str | list[str] | tuple[str] | None = Field(
73+
operation: Optional[str | list[str] | tuple[str]] = Field(
7474
None, description="One or more of VALID_OPERATIONS to apply in validating this parameter."
7575
)
76-
alias: str | None = Field(
76+
alias: Optional[str] = Field(
7777
None,
7878
description="If a str, an alternate name for a parameter to use when reporting invalid values, e.g., ENMAX instead of ENCUT.",
7979
)
8080
tolerance: float = Field(1e-4, description="The tolerance used when evaluating approximate float equality.")
81-
comment: str | None = Field(None, description="Additional information to pass to the user if a check fails.")
82-
warning: str | None = Field(None, description="Additional warnings to pass to the user if a check fails.")
81+
comment: Optional[str] = Field(None, description="Additional information to pass to the user if a check fails.")
82+
warning: Optional[str] = Field(None, description="Additional warnings to pass to the user if a check fails.")
8383
severity: Literal["reason", "warning"] = Field("reason", description="The severity of failing this check.")
8484

8585
@staticmethod
@@ -449,7 +449,7 @@ def format_val(val: Any) -> Any:
449449
),
450450
VaspParam(
451451
name="IBRION",
452-
value=0,
452+
value=-1,
453453
operation="in",
454454
alias="IBRION",
455455
tag="ionic",

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ select = "C, E, F, W, B, B950"
8080
explicit_package_bases = true
8181
namespace_packages = true
8282
ignore_missing_imports = true
83+
no_strict_optional = true
8384
plugins = ["pydantic.mypy"]
8485

8586
[tool.coverage.run]

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pydocstyle==6.1.1
66
flake8==7.1.1
77
pylint==3.3.1
88
black==24.8.0
9+
pydantic==2.11.3

requirements.txt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
pymatgen==2024.11.13
1+
pymatgen==2025.4.17
22
emmet-core==0.84.5
3-
pydantic==2.4.2
4-
pydantic-core==2.10.1
5-
pydantic-settings==2.2.1
3+
pydantic==2.11.3
64
typing-extensions==4.12.2
7-
monty==2025.1.9
5+
monty==2025.3.3
86
numpy==1.26.1
97
requests==2.32.3

0 commit comments

Comments
 (0)