matcalc 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {matcalc-0.3.2/src/matcalc.egg-info → matcalc-0.3.3}/PKG-INFO +9 -4
- {matcalc-0.3.2 → matcalc-0.3.3}/README.md +7 -2
- {matcalc-0.3.2 → matcalc-0.3.3}/pyproject.toml +5 -3
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/__init__.py +1 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_base.py +36 -7
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_elasticity.py +5 -3
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_eos.py +5 -6
- matcalc-0.3.3/src/matcalc/_neb.py +142 -0
- matcalc-0.3.3/src/matcalc/_phonon.py +222 -0
- matcalc-0.3.3/src/matcalc/_phonon3.py +237 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_qha.py +84 -37
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_relaxation.py +5 -4
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_stability.py +6 -5
- matcalc-0.3.3/src/matcalc/_surface.py +261 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/benchmark.py +6 -6
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/utils.py +6 -3
- {matcalc-0.3.2 → matcalc-0.3.3/src/matcalc.egg-info}/PKG-INFO +9 -4
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc.egg-info/SOURCES.txt +2 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc.egg-info/requires.txt +1 -1
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_chain.py +9 -11
- matcalc-0.3.3/tests/test_neb.py +31 -0
- matcalc-0.3.3/tests/test_surface.py +70 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_utils.py +0 -2
- matcalc-0.3.2/src/matcalc/_neb.py +0 -119
- matcalc-0.3.2/src/matcalc/_phonon.py +0 -174
- matcalc-0.3.2/src/matcalc/_phonon3.py +0 -184
- matcalc-0.3.2/tests/test_neb.py +0 -29
- {matcalc-0.3.2 → matcalc-0.3.3}/LICENSE +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/setup.cfg +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/_cli.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/config.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/elemental_refs/MatPES-PBE-Element-Refs.json.gz +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/elemental_refs/MatPES-r2SCAN-Element-Refs.json.gz +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/py.typed +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc/units.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc.egg-info/dependency_links.txt +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc.egg-info/entry_points.txt +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/src/matcalc.egg-info/top_level.txt +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_benchmark.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_cli.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_elasticity.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_eos.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_phonon.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_phonon3.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_qha.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_relaxation.py +0 -0
- {matcalc-0.3.2 → matcalc-0.3.3}/tests/test_stability.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matcalc
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Calculators for materials properties from the potential energy surface.
|
|
5
5
|
Author-email: Runze Liu <rul048@ucsd.edu>, Eliott Liu <elliottliu17@gmail.com>, Janosh Riebesell <janosh@lbl.gov>, Ji Qi <j1qi@ucsd.edu>, Shyue Ping Ong <ongsp@ucsd.edu>, Tsz Wai Ko <t1ko@ucsd.edu>
|
|
6
6
|
Keywords: AI,deep learning,force field,graph,interatomic potential,machine learning,materials,property prediction,science
|
|
@@ -39,7 +39,7 @@ Requires-Dist: deepmd-kit>=3.0.2; extra == "deepmd"
|
|
|
39
39
|
Provides-Extra: grace
|
|
40
40
|
Requires-Dist: tensorpotential>=0.5.1; extra == "grace"
|
|
41
41
|
Provides-Extra: maml
|
|
42
|
-
Requires-Dist: maml>=2025.
|
|
42
|
+
Requires-Dist: maml>=2025.4.3; extra == "maml"
|
|
43
43
|
Provides-Extra: orb
|
|
44
44
|
Requires-Dist: orb-models>=0.4.2; extra == "orb"
|
|
45
45
|
Requires-Dist: pynanoflann>=0.10.0; extra == "orb"
|
|
@@ -96,12 +96,12 @@ from pymatgen.ext.matproj import MPRester
|
|
|
96
96
|
|
|
97
97
|
mpr = MPRester()
|
|
98
98
|
si = mpr.get_structure_by_material_id("mp-149")
|
|
99
|
-
c = mtc.ElasticityCalc(
|
|
99
|
+
c = mtc.ElasticityCalc("TensorNet-MatPES-PBE-v2025.1-PES", relax_structure=True)
|
|
100
100
|
props = c.calc(si)
|
|
101
101
|
print(f"K_VRH = {props['bulk_modulus_vrh'] * 160.2176621} GPa")
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
The
|
|
104
|
+
The calculated `K_VRH` is about 102 GPa, in reasonably good agreement with the experimental and DFT values.
|
|
105
105
|
|
|
106
106
|
While we generally recommend users to specify exactly the model they would like to use, MatCalc provides useful
|
|
107
107
|
(case-insensitive) aliases to our recommended models for PBE and r2SCAN predictions. These can be loaded using:
|
|
@@ -213,6 +213,10 @@ results.to_csv("benchmark_results.csv")
|
|
|
213
213
|
These will usually take a long time to run. Running on HPC resources is recommended. Please set `n_samples` when
|
|
214
214
|
initializing the benchmark to limit the number of calculations to do some testing before running the full benchmark.
|
|
215
215
|
|
|
216
|
+
## Docker Images
|
|
217
|
+
|
|
218
|
+
Docker images with MatCalc and LAMMPS support are available at the [Materials Virtual Lab Docker Repository].
|
|
219
|
+
|
|
216
220
|
## Citing
|
|
217
221
|
|
|
218
222
|
A manuscript on `matcalc` is currently in the works. In the meantime, please see [`citation.cff`](citation.cff) or the GitHub
|
|
@@ -223,3 +227,4 @@ sidebar for a BibTeX and APA citation.
|
|
|
223
227
|
[MatPES]: https://matpes.ai
|
|
224
228
|
[MatCalc]: https://matcalc.ai
|
|
225
229
|
[ASE]: https://wiki.fysik.dtu.dk/ase/
|
|
230
|
+
[Materials Virtual Lab Docker Repository]: https://hub.docker.com/orgs/materialsvirtuallab/repositories
|
|
@@ -47,12 +47,12 @@ from pymatgen.ext.matproj import MPRester
|
|
|
47
47
|
|
|
48
48
|
mpr = MPRester()
|
|
49
49
|
si = mpr.get_structure_by_material_id("mp-149")
|
|
50
|
-
c = mtc.ElasticityCalc(
|
|
50
|
+
c = mtc.ElasticityCalc("TensorNet-MatPES-PBE-v2025.1-PES", relax_structure=True)
|
|
51
51
|
props = c.calc(si)
|
|
52
52
|
print(f"K_VRH = {props['bulk_modulus_vrh'] * 160.2176621} GPa")
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
The
|
|
55
|
+
The calculated `K_VRH` is about 102 GPa, in reasonably good agreement with the experimental and DFT values.
|
|
56
56
|
|
|
57
57
|
While we generally recommend users to specify exactly the model they would like to use, MatCalc provides useful
|
|
58
58
|
(case-insensitive) aliases to our recommended models for PBE and r2SCAN predictions. These can be loaded using:
|
|
@@ -164,6 +164,10 @@ results.to_csv("benchmark_results.csv")
|
|
|
164
164
|
These will usually take a long time to run. Running on HPC resources is recommended. Please set `n_samples` when
|
|
165
165
|
initializing the benchmark to limit the number of calculations to do some testing before running the full benchmark.
|
|
166
166
|
|
|
167
|
+
## Docker Images
|
|
168
|
+
|
|
169
|
+
Docker images with MatCalc and LAMMPS support are available at the [Materials Virtual Lab Docker Repository].
|
|
170
|
+
|
|
167
171
|
## Citing
|
|
168
172
|
|
|
169
173
|
A manuscript on `matcalc` is currently in the works. In the meantime, please see [`citation.cff`](citation.cff) or the GitHub
|
|
@@ -174,3 +178,4 @@ sidebar for a BibTeX and APA citation.
|
|
|
174
178
|
[MatPES]: https://matpes.ai
|
|
175
179
|
[MatCalc]: https://matcalc.ai
|
|
176
180
|
[ASE]: https://wiki.fysik.dtu.dk/ase/
|
|
181
|
+
[Materials Virtual Lab Docker Repository]: https://hub.docker.com/orgs/materialsvirtuallab/repositories
|
|
@@ -45,7 +45,7 @@ dependencies = [
|
|
|
45
45
|
"phonopy>=2.38.0",
|
|
46
46
|
"phono3py>=3.15.0",
|
|
47
47
|
]
|
|
48
|
-
version = "0.3.
|
|
48
|
+
version = "0.3.3"
|
|
49
49
|
|
|
50
50
|
[project.optional-dependencies]
|
|
51
51
|
phonon = ["seekpath"]
|
|
@@ -63,7 +63,7 @@ grace = [
|
|
|
63
63
|
"tensorpotential>=0.5.1",
|
|
64
64
|
]
|
|
65
65
|
maml = [
|
|
66
|
-
"maml>=2025.
|
|
66
|
+
"maml>=2025.4.3",
|
|
67
67
|
]
|
|
68
68
|
orb = [
|
|
69
69
|
"orb-models>=0.4.2",
|
|
@@ -94,10 +94,11 @@ dev = [
|
|
|
94
94
|
"coveralls>=4.0.1",
|
|
95
95
|
"ipython>=8.34.0",
|
|
96
96
|
"jupyter>=1.1.1",
|
|
97
|
-
"maml>=2025.
|
|
97
|
+
"maml>=2025.4.3",
|
|
98
98
|
"matgl>=1.2.3",
|
|
99
99
|
"matminer>=0.9.3",
|
|
100
100
|
"mp-api>=0.45.3",
|
|
101
|
+
"nbmake>=1.5.5",
|
|
101
102
|
"pre-commit>=4.2.0",
|
|
102
103
|
"pytest>=8.3.5",
|
|
103
104
|
"pytest-cov>=6.0.0",
|
|
@@ -113,6 +114,7 @@ maintain = [
|
|
|
113
114
|
"invoke>=2.2.0",
|
|
114
115
|
"sphinx>=8.1.3",
|
|
115
116
|
"sphinx-markdown-builder>=0.6.8",
|
|
117
|
+
"sphinx-rtd-theme>=3.0.2",
|
|
116
118
|
]
|
|
117
119
|
test-models = [
|
|
118
120
|
"mace-torch>=0.3.12",
|
|
@@ -18,6 +18,7 @@ from ._phonon3 import Phonon3Calc
|
|
|
18
18
|
from ._qha import QHACalc
|
|
19
19
|
from ._relaxation import RelaxCalc
|
|
20
20
|
from ._stability import EnergeticsCalc
|
|
21
|
+
from ._surface import SurfaceCalc
|
|
21
22
|
from .config import clear_cache
|
|
22
23
|
from .utils import UNIVERSAL_CALCULATORS, PESCalculator
|
|
23
24
|
|
|
@@ -7,9 +7,12 @@ from typing import TYPE_CHECKING, Any
|
|
|
7
7
|
|
|
8
8
|
from joblib import Parallel, delayed
|
|
9
9
|
|
|
10
|
+
from .utils import PESCalculator
|
|
11
|
+
|
|
10
12
|
if TYPE_CHECKING:
|
|
11
13
|
from collections.abc import Generator, Sequence
|
|
12
14
|
|
|
15
|
+
from ase.calculators.calculator import Calculator
|
|
13
16
|
from pymatgen.core import Structure
|
|
14
17
|
|
|
15
18
|
|
|
@@ -25,6 +28,33 @@ class PropCalc(abc.ABC):
|
|
|
25
28
|
on multiple structures using joblib.
|
|
26
29
|
"""
|
|
27
30
|
|
|
31
|
+
@property
|
|
32
|
+
def calculator(self) -> Calculator:
|
|
33
|
+
"""
|
|
34
|
+
This method returns the Calculator object associated with the current instance.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
None
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Calculator: The Calculator object associated with the current instance.
|
|
41
|
+
"""
|
|
42
|
+
return self._pes_calculator
|
|
43
|
+
|
|
44
|
+
@calculator.setter
|
|
45
|
+
def calculator(self, val: str | Calculator) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Set the calculator for PES calculation.
|
|
48
|
+
|
|
49
|
+
Parameters:
|
|
50
|
+
val (str | Calculator): A string path to load a PESCalculator or a PESCalculator object.
|
|
51
|
+
|
|
52
|
+
Return:
|
|
53
|
+
None
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
self._pes_calculator = PESCalculator.load_universal(val) if isinstance(val, str) else val
|
|
57
|
+
|
|
28
58
|
@abc.abstractmethod
|
|
29
59
|
def calc(self, structure: Structure | dict[str, Any]) -> dict[str, Any]:
|
|
30
60
|
"""
|
|
@@ -93,16 +123,15 @@ class PropCalc(abc.ABC):
|
|
|
93
123
|
the `allow_errors` flag).
|
|
94
124
|
"""
|
|
95
125
|
parallel = Parallel(n_jobs=n_jobs, return_as="generator", **kwargs)
|
|
96
|
-
if allow_errors:
|
|
97
126
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
127
|
+
def _func(s: Structure) -> dict | None:
|
|
128
|
+
try:
|
|
129
|
+
return self.calc(s)
|
|
130
|
+
except Exception as ex:
|
|
131
|
+
if allow_errors:
|
|
102
132
|
return None
|
|
133
|
+
raise ex # noqa:TRY201
|
|
103
134
|
|
|
104
|
-
else:
|
|
105
|
-
_func = self.calc # type: ignore[assignment]
|
|
106
135
|
return parallel(delayed(_func)(s) for s in structures)
|
|
107
136
|
|
|
108
137
|
|
|
@@ -49,7 +49,7 @@ class ElasticityCalc(PropCalc):
|
|
|
49
49
|
|
|
50
50
|
def __init__(
|
|
51
51
|
self,
|
|
52
|
-
calculator: Calculator,
|
|
52
|
+
calculator: Calculator | str,
|
|
53
53
|
*,
|
|
54
54
|
norm_strains: Sequence[float] | float = (-0.01, -0.005, 0.005, 0.01),
|
|
55
55
|
shear_strains: Sequence[float] | float = (-0.06, -0.03, 0.03, 0.06),
|
|
@@ -65,7 +65,9 @@ class ElasticityCalc(PropCalc):
|
|
|
65
65
|
and control relaxation behavior for structures. Validates input parameters to ensure
|
|
66
66
|
appropriate constraints are maintained.
|
|
67
67
|
|
|
68
|
-
:param calculator:
|
|
68
|
+
:param calculator: An ASE calculator object used to perform energy and force
|
|
69
|
+
calculations. If string is provided, the corresponding universal calculator is loaded.
|
|
70
|
+
:type calculator: Calculator | str
|
|
69
71
|
:param norm_strains: Sequence of normalized strain values applied during deformation.
|
|
70
72
|
Can also be a single float. Must not be empty or contain zero.
|
|
71
73
|
:param shear_strains: Sequence of shear strain values applied during deformation.
|
|
@@ -81,7 +83,7 @@ class ElasticityCalc(PropCalc):
|
|
|
81
83
|
:param relax_calc_kwargs: Optional dictionary containing keyword arguments for structure
|
|
82
84
|
relaxation calculations.
|
|
83
85
|
"""
|
|
84
|
-
self.calculator = calculator
|
|
86
|
+
self.calculator = calculator # type: ignore[assignment]
|
|
85
87
|
self.norm_strains = tuple(np.array([1]) * np.asarray(norm_strains))
|
|
86
88
|
self.shear_strains = tuple(np.array([1]) * np.asarray(shear_strains))
|
|
87
89
|
if len(self.norm_strains) == 0:
|
|
@@ -49,7 +49,7 @@ class EOSCalc(PropCalc):
|
|
|
49
49
|
|
|
50
50
|
def __init__(
|
|
51
51
|
self,
|
|
52
|
-
calculator: Calculator,
|
|
52
|
+
calculator: Calculator | str,
|
|
53
53
|
*,
|
|
54
54
|
optimizer: Optimizer | str = "FIRE",
|
|
55
55
|
max_steps: int = 500,
|
|
@@ -65,10 +65,9 @@ class EOSCalc(PropCalc):
|
|
|
65
65
|
simulation parameters, structural relaxation options, and optimizations
|
|
66
66
|
with specified constraints and tolerances.
|
|
67
67
|
|
|
68
|
-
:param calculator:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
:type calculator: Calculator
|
|
68
|
+
:param calculator: An ASE calculator object used to perform energy and force
|
|
69
|
+
calculations. If string is provided, the corresponding universal calculator is loaded.
|
|
70
|
+
:type calculator: Calculator | str
|
|
72
71
|
:param optimizer: The optimization algorithm used for structural relaxations
|
|
73
72
|
or energy minimizations. Can be an optimizer object or the string name
|
|
74
73
|
of the algorithm. Default is "FIRE".
|
|
@@ -92,7 +91,7 @@ class EOSCalc(PropCalc):
|
|
|
92
91
|
relaxation calculation process. Default is None.
|
|
93
92
|
:type relax_calc_kwargs: dict | None, optional
|
|
94
93
|
"""
|
|
95
|
-
self.calculator = calculator
|
|
94
|
+
self.calculator = calculator # type: ignore[assignment]
|
|
96
95
|
self.optimizer = optimizer
|
|
97
96
|
self.relax_structure = relax_structure
|
|
98
97
|
self.n_points = n_points
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""NEB calculations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from ase.io import Trajectory
|
|
9
|
+
from ase.mep import NEBTools
|
|
10
|
+
from ase.neb import NEB
|
|
11
|
+
from ase.utils.forcecurve import fit_images
|
|
12
|
+
from pymatgen.core import Structure
|
|
13
|
+
from pymatgen.io.ase import AseAtomsAdaptor
|
|
14
|
+
|
|
15
|
+
from ._base import PropCalc
|
|
16
|
+
from .utils import get_ase_optimizer
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from ase import Atoms
|
|
20
|
+
from ase.calculators.calculator import Calculator
|
|
21
|
+
from ase.optimize.optimize import Optimizer
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NEBCalc(PropCalc):
|
|
25
|
+
"""NEB calculator."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
calculator: Calculator | str,
|
|
30
|
+
*,
|
|
31
|
+
optimizer: str | Optimizer = "BFGS",
|
|
32
|
+
traj_folder: str | None = None,
|
|
33
|
+
interval: int = 1,
|
|
34
|
+
climb: bool = True,
|
|
35
|
+
fmax: float = 0.1,
|
|
36
|
+
max_steps: int = 1000,
|
|
37
|
+
**kwargs: Any,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Initialize the instance of the class.
|
|
40
|
+
|
|
41
|
+
Parameters:
|
|
42
|
+
calculator (Calculator | str): An ASE calculator object used to perform energy and force
|
|
43
|
+
calculations. If string is provided, the corresponding universal calculator is loaded.
|
|
44
|
+
optimizer (str | Optimizer, optional): The optimization algorithm to use. Defaults to "BFGS".
|
|
45
|
+
traj_folder (str | None, optional): The folder to save trajectory information. Defaults to None.
|
|
46
|
+
interval (int, optional): The interval for recording trajectory information. Defaults to 1.
|
|
47
|
+
climb (bool, optional): Whether to perform climbing image nudged elastic band (CI-NEB). Defaults to True.
|
|
48
|
+
fmax (float, optional): The maximum force tolerance for convergence. Defaults to 0.1.
|
|
49
|
+
max_steps (int, optional): The maximum number of optimization steps. Defaults to 1000.
|
|
50
|
+
**kwargs (Any): Additional keyword arguments.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
None
|
|
54
|
+
"""
|
|
55
|
+
self.calculator = calculator # type: ignore[assignment]
|
|
56
|
+
|
|
57
|
+
self.traj_folder = traj_folder
|
|
58
|
+
self.interval = interval
|
|
59
|
+
self.climb = climb
|
|
60
|
+
self.optimizer = get_ase_optimizer(optimizer)
|
|
61
|
+
self.fmax = fmax
|
|
62
|
+
self.max_steps = max_steps
|
|
63
|
+
self.kwargs = kwargs
|
|
64
|
+
|
|
65
|
+
def calc_images(
|
|
66
|
+
self,
|
|
67
|
+
start_struct: Structure,
|
|
68
|
+
end_struct: Structure,
|
|
69
|
+
*,
|
|
70
|
+
n_images: int = 7,
|
|
71
|
+
interpolate_lattices: bool = False,
|
|
72
|
+
autosort_tol: float = 0.5,
|
|
73
|
+
) -> dict[str, Any]:
|
|
74
|
+
"""Calculate NEB images between given start and end structures.
|
|
75
|
+
|
|
76
|
+
Parameters:
|
|
77
|
+
start_struct (Structure): Initial structure.
|
|
78
|
+
end_struct (Structure): Final structure.
|
|
79
|
+
n_images (int): Number of images to calculate (default is 7).
|
|
80
|
+
interpolate_lattices (bool): Whether to interpolate lattices between start and end structures (default is
|
|
81
|
+
False).
|
|
82
|
+
autosort_tol (float): Tolerance for autosorting the images (default is 0.5).
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
NEBCalc: NEB calculation object containing the interpolated images.
|
|
86
|
+
"""
|
|
87
|
+
images = start_struct.interpolate(
|
|
88
|
+
end_struct,
|
|
89
|
+
nimages=n_images + 1,
|
|
90
|
+
interpolate_lattices=interpolate_lattices,
|
|
91
|
+
pbc=False,
|
|
92
|
+
autosort_tol=autosort_tol,
|
|
93
|
+
)
|
|
94
|
+
return self.calc({f"image{i:02d}": s for i, s in enumerate(images)})
|
|
95
|
+
|
|
96
|
+
def calc(
|
|
97
|
+
self,
|
|
98
|
+
structure: Structure | dict[str, Any],
|
|
99
|
+
) -> dict[str, Any]:
|
|
100
|
+
"""Calculate the energy barrier using the nudged elastic band method.
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
- structure: A dictionary containing the images with keys 'image0', 'image1', etc. Must be of type dict.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
dict:
|
|
107
|
+
- "barrier" (float): The energy barrier of the reaction pathway.
|
|
108
|
+
- "force" (float): The force exerted on the atoms during the NEB calculation.
|
|
109
|
+
- "mep" (dict): a dictionary containing the images and their respective energies.
|
|
110
|
+
"""
|
|
111
|
+
if not isinstance(structure, dict):
|
|
112
|
+
raise ValueError( # noqa:TRY004
|
|
113
|
+
"For NEB calculations, structure must be a dict containing the images with keys image00, image01, etc."
|
|
114
|
+
)
|
|
115
|
+
images: list[Atoms] = []
|
|
116
|
+
for _, image in sorted(structure.items(), key=lambda x: x[0]):
|
|
117
|
+
atoms = image.to_ase_atoms() if isinstance(image, Structure) else image
|
|
118
|
+
atoms.calc = self.calculator
|
|
119
|
+
images.append(atoms)
|
|
120
|
+
|
|
121
|
+
self.neb = NEB(images, climb=self.climb, allow_shared_calculator=True, **self.kwargs)
|
|
122
|
+
optimizer = self.optimizer(self.neb) # type:ignore[operator]
|
|
123
|
+
if self.traj_folder is not None:
|
|
124
|
+
os.makedirs(self.traj_folder, exist_ok=True)
|
|
125
|
+
for idx, img in enumerate(images):
|
|
126
|
+
optimizer.attach(
|
|
127
|
+
Trajectory(f"{self.traj_folder}/image-{idx}.traj", "w", img),
|
|
128
|
+
interval=self.interval,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
optimizer.run(fmax=self.fmax, steps=self.max_steps)
|
|
132
|
+
neb_tool = NEBTools(self.neb.images)
|
|
133
|
+
data = neb_tool.get_barrier() # add structures
|
|
134
|
+
result = {"barrier": data[0], "force": data[1]}
|
|
135
|
+
|
|
136
|
+
energies = fit_images(self.neb.images).energies
|
|
137
|
+
mep = {
|
|
138
|
+
f"image{i:02d}": {"structure": AseAtomsAdaptor.get_structure(image), "energy": energy}
|
|
139
|
+
for i, (image, energy) in enumerate(zip(self.neb.images, energies, strict=False))
|
|
140
|
+
}
|
|
141
|
+
result["mep"] = mep
|
|
142
|
+
return result
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""Calculator for phonon properties."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
import phonopy
|
|
8
|
+
from phonopy.file_IO import write_FORCE_CONSTANTS as write_force_constants
|
|
9
|
+
from pymatgen.io.ase import AseAtomsAdaptor
|
|
10
|
+
from pymatgen.io.phonopy import get_phonopy_structure, get_pmg_structure
|
|
11
|
+
|
|
12
|
+
from ._base import PropCalc
|
|
13
|
+
from ._relaxation import RelaxCalc
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from ase.calculators.calculator import Calculator
|
|
20
|
+
from numpy.typing import ArrayLike
|
|
21
|
+
from phonopy.structure.atoms import PhonopyAtoms
|
|
22
|
+
from pymatgen.core import Structure
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PhononCalc(PropCalc):
|
|
26
|
+
"""
|
|
27
|
+
A class for phonon and thermal property calculations using phonopy.
|
|
28
|
+
|
|
29
|
+
The `PhononCalc` class extends the `PropCalc` class to provide
|
|
30
|
+
functionalities for calculating phonon properties and thermal properties
|
|
31
|
+
of a given structure using the phonopy library. It includes options for
|
|
32
|
+
structural relaxation before phonon property determination, as well as
|
|
33
|
+
methods to export calculated properties to various output files for
|
|
34
|
+
further analysis or visualization.
|
|
35
|
+
|
|
36
|
+
:ivar calculator: A calculator object or a string specifying the
|
|
37
|
+
computational backend to be used.
|
|
38
|
+
:type calculator: Calculator | str
|
|
39
|
+
:ivar atom_disp: Magnitude of atomic displacements for phonon
|
|
40
|
+
calculations.
|
|
41
|
+
:type atom_disp: float
|
|
42
|
+
:ivar supercell_matrix: Array defining the transformation matrix to
|
|
43
|
+
construct supercells for phonon calculations.
|
|
44
|
+
:type supercell_matrix: ArrayLike
|
|
45
|
+
:ivar t_step: Temperature step for thermal property calculations in
|
|
46
|
+
Kelvin.
|
|
47
|
+
:type t_step: float
|
|
48
|
+
:ivar t_max: Maximum temperature for thermal property calculations in
|
|
49
|
+
Kelvin.
|
|
50
|
+
:type t_max: float
|
|
51
|
+
:ivar t_min: Minimum temperature for thermal property calculations in
|
|
52
|
+
Kelvin.
|
|
53
|
+
:type t_min: float
|
|
54
|
+
:ivar fmax: Maximum force convergence criterion for structural relaxation.
|
|
55
|
+
:type fmax: float
|
|
56
|
+
:ivar optimizer: String specifying the optimizer type to be used for
|
|
57
|
+
structural relaxation.
|
|
58
|
+
:type optimizer: str
|
|
59
|
+
:ivar relax_structure: Boolean flag to determine whether to relax the
|
|
60
|
+
structure before phonon calculation.
|
|
61
|
+
:type relax_structure: bool
|
|
62
|
+
:ivar relax_calc_kwargs: Optional dictionary containing additional
|
|
63
|
+
arguments for the structural relaxation calculation.
|
|
64
|
+
:type relax_calc_kwargs: dict | None
|
|
65
|
+
:ivar write_force_constants: Path, boolean, or string specifying whether
|
|
66
|
+
to write the calculated force constants to an output file, and the
|
|
67
|
+
path or name of the file if applicable.
|
|
68
|
+
:type write_force_constants: bool | str | Path
|
|
69
|
+
:ivar write_band_structure: Path, boolean, or string specifying whether
|
|
70
|
+
to write the calculated phonon band structure to an output file,
|
|
71
|
+
and the path or name of the file if applicable.
|
|
72
|
+
:type write_band_structure: bool | str | Path
|
|
73
|
+
:ivar write_total_dos: Path, boolean, or string specifying whether to
|
|
74
|
+
write the calculated total density of states (DOS) to an output
|
|
75
|
+
file, and the path or name of the file if applicable.
|
|
76
|
+
:type write_total_dos: bool | str | Path
|
|
77
|
+
:ivar write_phonon: Path, boolean, or string specifying whether to write
|
|
78
|
+
the calculated phonon properties (e.g., phonon.yaml) to an output
|
|
79
|
+
file, and the path or name of the file if applicable.
|
|
80
|
+
:type write_phonon: bool | str | Path
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
calculator: Calculator | str,
|
|
86
|
+
*,
|
|
87
|
+
atom_disp: float = 0.015,
|
|
88
|
+
supercell_matrix: ArrayLike = ((2, 0, 0), (0, 2, 0), (0, 0, 2)),
|
|
89
|
+
t_step: float = 10,
|
|
90
|
+
t_max: float = 1000,
|
|
91
|
+
t_min: float = 0,
|
|
92
|
+
fmax: float = 0.1,
|
|
93
|
+
optimizer: str = "FIRE",
|
|
94
|
+
relax_structure: bool = True,
|
|
95
|
+
relax_calc_kwargs: dict | None = None,
|
|
96
|
+
write_force_constants: bool | str | Path = False,
|
|
97
|
+
write_band_structure: bool | str | Path = False,
|
|
98
|
+
write_total_dos: bool | str | Path = False,
|
|
99
|
+
write_phonon: bool | str | Path = True,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Initializes the class with configuration for the phonon calculations. The initialization parameters control
|
|
103
|
+
the behavior of structural relaxation, thermal properties, force calculations, and output file generation.
|
|
104
|
+
The class allows for customization of key parameters to facilitate the study of material behaviors.
|
|
105
|
+
|
|
106
|
+
:param calculator: The calculator object or string name specifying the calculation backend to use.
|
|
107
|
+
:param atom_disp: Atom displacement to be used for finite difference calculation of force constants.
|
|
108
|
+
:param supercell_matrix: Transformation matrix to define the supercell for the calculation.
|
|
109
|
+
:param t_step: Temperature step for thermal property calculations.
|
|
110
|
+
:param t_max: Maximum temperature for thermal property calculations.
|
|
111
|
+
:param t_min: Minimum temperature for thermal property calculations.
|
|
112
|
+
:param fmax: Maximum force during structure relaxation, used as a convergence criterion.
|
|
113
|
+
:param optimizer: Name of the optimization algorithm for structural relaxation.
|
|
114
|
+
:param relax_structure: Flag to indicate whether structure relaxation should be performed before calculations.
|
|
115
|
+
:param relax_calc_kwargs: Additional keyword arguments for relaxation phase calculations.
|
|
116
|
+
:param write_force_constants: File path or boolean flag to write force constants.
|
|
117
|
+
Defaults to "force_constants".
|
|
118
|
+
:param write_band_structure: File path or boolean flag to write band structure data.
|
|
119
|
+
Defaults to "band_structure.yaml".
|
|
120
|
+
:param write_total_dos: File path or boolean flag to write total density of states (DOS) data.
|
|
121
|
+
Defaults to "total_dos.dat".
|
|
122
|
+
:param write_phonon: File path or boolean flag to write phonon data. Defaults to "phonon.yaml".
|
|
123
|
+
"""
|
|
124
|
+
self.calculator = calculator # type: ignore[assignment]
|
|
125
|
+
self.atom_disp = atom_disp
|
|
126
|
+
self.supercell_matrix = supercell_matrix
|
|
127
|
+
self.t_step = t_step
|
|
128
|
+
self.t_max = t_max
|
|
129
|
+
self.t_min = t_min
|
|
130
|
+
self.fmax = fmax
|
|
131
|
+
self.optimizer = optimizer
|
|
132
|
+
self.relax_structure = relax_structure
|
|
133
|
+
self.relax_calc_kwargs = relax_calc_kwargs
|
|
134
|
+
self.write_force_constants = write_force_constants
|
|
135
|
+
self.write_band_structure = write_band_structure
|
|
136
|
+
self.write_total_dos = write_total_dos
|
|
137
|
+
self.write_phonon = write_phonon
|
|
138
|
+
|
|
139
|
+
# Set default paths for output files.
|
|
140
|
+
for key, val, default_path in (
|
|
141
|
+
("write_force_constants", self.write_force_constants, "force_constants"),
|
|
142
|
+
("write_band_structure", self.write_band_structure, "band_structure.yaml"),
|
|
143
|
+
("write_total_dos", self.write_total_dos, "total_dos.dat"),
|
|
144
|
+
("write_phonon", self.write_phonon, "phonon.yaml"),
|
|
145
|
+
):
|
|
146
|
+
setattr(self, key, str({True: default_path, False: ""}.get(val, val))) # type: ignore[arg-type]
|
|
147
|
+
|
|
148
|
+
def calc(self, structure: Structure | dict[str, Any]) -> dict:
|
|
149
|
+
"""Calculates thermal properties of Pymatgen structure with phonopy.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
structure: Pymatgen structure.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
{
|
|
156
|
+
phonon: Phonopy object with force constants produced
|
|
157
|
+
thermal_properties:
|
|
158
|
+
{
|
|
159
|
+
temperatures: list of temperatures in Kelvin,
|
|
160
|
+
free_energy: list of Helmholtz free energies at corresponding temperatures in kJ/mol,
|
|
161
|
+
entropy: list of entropies at corresponding temperatures in J/K/mol,
|
|
162
|
+
heat_capacity: list of heat capacities at constant volume at corresponding temperatures in J/K/mol,
|
|
163
|
+
The units are originally documented in phonopy.
|
|
164
|
+
See phonopy.Phonopy.run_thermal_properties()
|
|
165
|
+
(https://github.com/phonopy/phonopy/blob/develop/phonopy/api_phonopy.py#L2591)
|
|
166
|
+
-> phonopy.phonon.thermal_properties.ThermalProperties.run()
|
|
167
|
+
(https://github.com/phonopy/phonopy/blob/develop/phonopy/phonon/thermal_properties.py#L498)
|
|
168
|
+
-> phonopy.phonon.thermal_properties.ThermalPropertiesBase.run_free_energy()
|
|
169
|
+
(https://github.com/phonopy/phonopy/blob/develop/phonopy/phonon/thermal_properties.py#L217)
|
|
170
|
+
phonopy.phonon.thermal_properties.ThermalPropertiesBase.run_entropy()
|
|
171
|
+
(https://github.com/phonopy/phonopy/blob/develop/phonopy/phonon/thermal_properties.py#L233)
|
|
172
|
+
phonopy.phonon.thermal_properties.ThermalPropertiesBase.run_heat_capacity()
|
|
173
|
+
(https://github.com/phonopy/phonopy/blob/develop/phonopy/phonon/thermal_properties.py#L225)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
"""
|
|
177
|
+
result = super().calc(structure)
|
|
178
|
+
structure_in: Structure = result["final_structure"]
|
|
179
|
+
|
|
180
|
+
if self.relax_structure:
|
|
181
|
+
relaxer = RelaxCalc(
|
|
182
|
+
self.calculator, fmax=self.fmax, optimizer=self.optimizer, **(self.relax_calc_kwargs or {})
|
|
183
|
+
)
|
|
184
|
+
result |= relaxer.calc(structure_in)
|
|
185
|
+
structure_in = result["final_structure"]
|
|
186
|
+
cell = get_phonopy_structure(structure_in)
|
|
187
|
+
phonon = phonopy.Phonopy(cell, self.supercell_matrix) # type: ignore[arg-type]
|
|
188
|
+
phonon.generate_displacements(distance=self.atom_disp)
|
|
189
|
+
disp_supercells = phonon.supercells_with_displacements
|
|
190
|
+
phonon.forces = [ # type: ignore[assignment]
|
|
191
|
+
_calc_forces(self.calculator, supercell)
|
|
192
|
+
for supercell in disp_supercells # type:ignore[union-attr]
|
|
193
|
+
if supercell is not None
|
|
194
|
+
]
|
|
195
|
+
phonon.produce_force_constants()
|
|
196
|
+
phonon.run_mesh()
|
|
197
|
+
phonon.run_thermal_properties(t_step=self.t_step, t_max=self.t_max, t_min=self.t_min)
|
|
198
|
+
if self.write_force_constants:
|
|
199
|
+
write_force_constants(phonon.force_constants, filename=self.write_force_constants)
|
|
200
|
+
if self.write_band_structure:
|
|
201
|
+
phonon.auto_band_structure(write_yaml=True, filename=self.write_band_structure)
|
|
202
|
+
if self.write_total_dos:
|
|
203
|
+
phonon.auto_total_dos(write_dat=True, filename=self.write_total_dos)
|
|
204
|
+
if self.write_phonon:
|
|
205
|
+
phonon.save(filename=self.write_phonon)
|
|
206
|
+
return result | {"phonon": phonon, "thermal_properties": phonon.get_thermal_properties_dict()}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _calc_forces(calculator: Calculator, supercell: PhonopyAtoms) -> ArrayLike:
|
|
210
|
+
"""Helper to compute forces on a structure.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
calculator: ASE Calculator
|
|
214
|
+
supercell: Supercell from phonopy.
|
|
215
|
+
|
|
216
|
+
Return:
|
|
217
|
+
forces
|
|
218
|
+
"""
|
|
219
|
+
struct = get_pmg_structure(supercell)
|
|
220
|
+
atoms = AseAtomsAdaptor.get_atoms(struct)
|
|
221
|
+
atoms.calc = calculator
|
|
222
|
+
return atoms.get_forces()
|