matcalc 0.2.0__tar.gz → 0.2.2__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.
Files changed (40) hide show
  1. {matcalc-0.2.0/src/matcalc.egg-info → matcalc-0.2.2}/PKG-INFO +18 -13
  2. {matcalc-0.2.0 → matcalc-0.2.2}/README.md +13 -12
  3. {matcalc-0.2.0 → matcalc-0.2.2}/pyproject.toml +20 -3
  4. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/__init__.py +4 -1
  5. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/base.py +63 -0
  6. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/benchmark.py +124 -36
  7. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/cli.py +1 -1
  8. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/config.py +1 -1
  9. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/elasticity.py +10 -4
  10. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/eos.py +3 -1
  11. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/utils.py +3 -60
  12. {matcalc-0.2.0 → matcalc-0.2.2/src/matcalc.egg-info}/PKG-INFO +18 -13
  13. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc.egg-info/requires.txt +6 -0
  14. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_benchmark.py +9 -21
  15. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_chain.py +4 -2
  16. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_cli.py +10 -1
  17. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_elasticity.py +1 -0
  18. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_neb.py +1 -0
  19. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_phonon.py +1 -0
  20. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_qha.py +1 -0
  21. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_relaxation.py +15 -0
  22. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_stability.py +1 -0
  23. matcalc-0.2.2/tests/test_utils.py +168 -0
  24. matcalc-0.2.0/tests/test_utils.py +0 -157
  25. {matcalc-0.2.0 → matcalc-0.2.2}/LICENSE +0 -0
  26. {matcalc-0.2.0 → matcalc-0.2.2}/setup.cfg +0 -0
  27. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/elemental_refs/MatPES-PBE-Element-Refs.json.gz +0 -0
  28. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/elemental_refs/MatPES-r2SCAN-Element-Refs.json.gz +0 -0
  29. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/neb.py +0 -0
  30. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/phonon.py +0 -0
  31. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/py.typed +0 -0
  32. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/qha.py +0 -0
  33. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/relaxation.py +0 -0
  34. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/stability.py +0 -0
  35. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc/units.py +0 -0
  36. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc.egg-info/SOURCES.txt +0 -0
  37. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc.egg-info/dependency_links.txt +0 -0
  38. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc.egg-info/entry_points.txt +0 -0
  39. {matcalc-0.2.0 → matcalc-0.2.2}/src/matcalc.egg-info/top_level.txt +0 -0
  40. {matcalc-0.2.0 → matcalc-0.2.2}/tests/test_eos.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matcalc
3
- Version: 0.2.0
3
+ Version: 0.2.2
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
@@ -41,11 +41,15 @@ Provides-Extra: grace
41
41
  Requires-Dist: tensorpotential>=0.5.1; extra == "grace"
42
42
  Provides-Extra: matgl
43
43
  Requires-Dist: matgl>=1.2.3; extra == "matgl"
44
+ Provides-Extra: orb
45
+ Requires-Dist: orb-models>=0.4.2; extra == "orb"
46
+ Provides-Extra: ace
47
+ Requires-Dist: pyace>=0.1.1; extra == "ace"
44
48
  Dynamic: license-file
45
49
 
46
50
  <h1 align="center">
47
- <img src="https://github.com/materialsvirtuallab/matcalc/assets/30958850/89486f2f-73fb-40fb-803a-dfafe510eb6d" width="100" alt="MatCalc logo" style="vertical-align: middle;" /><br>
48
- MatCalc
51
+ <img src="https://raw.githubusercontent.com/materialsvirtuallab/matcalc/refs/heads/main/docs/assets/matcalc.png"
52
+ width="200" alt="MatCalc" style="vertical-align: middle;" /><br>
49
53
  </h1>
50
54
 
51
55
  [![Testing](https://github.com/materialsvirtuallab/matcalc/workflows/Testing/badge.svg)](https://github.com/materialsvirtuallab/matcalc/workflows/Testing/badge.svg)
@@ -87,12 +91,12 @@ MatCalc provides convenient methods to quickly compute properties, using a minim
87
91
  an example of a computation of the elastic constants of Si using the `TensorNet-MatPES-PBE-v2025.1-PES` universal MLIP.
88
92
 
89
93
  ```python
90
- from matcalc import PESCalculator, ElasticityCalc
94
+ from matcalc import PESCalculator, ElasticityCalc, load_up
91
95
  from pymatgen.ext.matproj import MPRester
92
96
 
93
97
  mpr = MPRester()
94
98
  si = mpr.get_structure_by_material_id("mp-149")
95
- c = ElasticityCalc(PESCalculator.load_universal("TensorNet-MatPES-PBE-v2025.1-PES"), relax_structure=True)
99
+ c = ElasticityCalc(load_up("TensorNet-MatPES-PBE-v2025.1-PES"), relax_structure=True)
96
100
  props = c.calc(si)
97
101
  print(f"K_VRH = {props['bulk_modulus_vrh'] * 160.2176621} GPa")
98
102
  ```
@@ -103,9 +107,9 @@ While we generally recommend users to specify exactly the model they would like
103
107
  (case-insensitive) aliases to our recommended models for PBE and r2SCAN predictions. These can be loaded using:
104
108
 
105
109
  ```python
106
- from matcalc import PESCalculator
107
- pbe_calculator = PESCalculator.load_universal("pbe")
108
- r2scan_calculator = PESCalculator.load_universal("r2scan")
110
+ from matcalc import load_up
111
+ pbe_calculator = load_up("pbe")
112
+ r2scan_calculator = load_up("r2scan")
109
113
  ```
110
114
 
111
115
  At the time of writing, these are the TensorNet-MatPES models. However, these recommendations may updated as improved
@@ -138,9 +142,9 @@ computed by all steps. Note that the `relax_structure` should be set to False in
138
142
  do not redo the relatively expensive relaxation.
139
143
 
140
144
  ```python
141
- from matcalc import PESCalculator, ElasticityCalc, RelaxCalc, EnergeticsCalc
145
+ from matcalc import load_up, ElasticityCalc, RelaxCalc, EnergeticsCalc, ChainedCalc
142
146
  import numpy as np
143
- calculator = PESCalculator.load_universal("pbe")
147
+ calculator = load_up("pbe")
144
148
  relax_calc = RelaxCalc(
145
149
  calculator,
146
150
  optimizer="FIRE",
@@ -160,7 +164,8 @@ elast_calc = ElasticityCalc(
160
164
  relax_structure=False, # Since we are chaining, we do not need to relax structure in later steps.
161
165
  relax_deformed_structures=True,
162
166
  )
163
- results = elast_calc.calc(energetics_calc.calc(relax_calc.calc(structure)))
167
+ prop_calc = ChainedCalc([relax_calc, energetics_calc, elast_calc])
168
+ results = prop_calc.calc(structure)
164
169
  ```
165
170
 
166
171
  Chaining can also be used with the `calc_many` method, with parallelization.
@@ -181,8 +186,8 @@ the `MatCalc-Benchmark`.
181
186
  For example, the following code can be used to run the ElasticityBenchmark on `TensorNet-MatPES-PBE-v2025.1-PES` UMLIP.
182
187
 
183
188
  ```python
184
- from matcalc import PESCalculator
185
- calculator = PESCalculator.load_universal("TensorNet-MatPES-PBE-v2025.1-PES")
189
+ from matcalc import load_up
190
+ calculator = load_up("TensorNet-MatPES-PBE-v2025.1-PES")
186
191
  from matcalc.benchmark import ElasticityBenchmark
187
192
  benchmark = ElasticityBenchmark(fmax=0.05, relax_structure=True)
188
193
  results = benchmark.run(calculator, "TensorNet-MatPES")
@@ -1,6 +1,6 @@
1
1
  <h1 align="center">
2
- <img src="https://github.com/materialsvirtuallab/matcalc/assets/30958850/89486f2f-73fb-40fb-803a-dfafe510eb6d" width="100" alt="MatCalc logo" style="vertical-align: middle;" /><br>
3
- MatCalc
2
+ <img src="https://raw.githubusercontent.com/materialsvirtuallab/matcalc/refs/heads/main/docs/assets/matcalc.png"
3
+ width="200" alt="MatCalc" style="vertical-align: middle;" /><br>
4
4
  </h1>
5
5
 
6
6
  [![Testing](https://github.com/materialsvirtuallab/matcalc/workflows/Testing/badge.svg)](https://github.com/materialsvirtuallab/matcalc/workflows/Testing/badge.svg)
@@ -42,12 +42,12 @@ MatCalc provides convenient methods to quickly compute properties, using a minim
42
42
  an example of a computation of the elastic constants of Si using the `TensorNet-MatPES-PBE-v2025.1-PES` universal MLIP.
43
43
 
44
44
  ```python
45
- from matcalc import PESCalculator, ElasticityCalc
45
+ from matcalc import PESCalculator, ElasticityCalc, load_up
46
46
  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 = ElasticityCalc(PESCalculator.load_universal("TensorNet-MatPES-PBE-v2025.1-PES"), relax_structure=True)
50
+ c = ElasticityCalc(load_up("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
  ```
@@ -58,9 +58,9 @@ While we generally recommend users to specify exactly the model they would like
58
58
  (case-insensitive) aliases to our recommended models for PBE and r2SCAN predictions. These can be loaded using:
59
59
 
60
60
  ```python
61
- from matcalc import PESCalculator
62
- pbe_calculator = PESCalculator.load_universal("pbe")
63
- r2scan_calculator = PESCalculator.load_universal("r2scan")
61
+ from matcalc import load_up
62
+ pbe_calculator = load_up("pbe")
63
+ r2scan_calculator = load_up("r2scan")
64
64
  ```
65
65
 
66
66
  At the time of writing, these are the TensorNet-MatPES models. However, these recommendations may updated as improved
@@ -93,9 +93,9 @@ computed by all steps. Note that the `relax_structure` should be set to False in
93
93
  do not redo the relatively expensive relaxation.
94
94
 
95
95
  ```python
96
- from matcalc import PESCalculator, ElasticityCalc, RelaxCalc, EnergeticsCalc
96
+ from matcalc import load_up, ElasticityCalc, RelaxCalc, EnergeticsCalc, ChainedCalc
97
97
  import numpy as np
98
- calculator = PESCalculator.load_universal("pbe")
98
+ calculator = load_up("pbe")
99
99
  relax_calc = RelaxCalc(
100
100
  calculator,
101
101
  optimizer="FIRE",
@@ -115,7 +115,8 @@ elast_calc = ElasticityCalc(
115
115
  relax_structure=False, # Since we are chaining, we do not need to relax structure in later steps.
116
116
  relax_deformed_structures=True,
117
117
  )
118
- results = elast_calc.calc(energetics_calc.calc(relax_calc.calc(structure)))
118
+ prop_calc = ChainedCalc([relax_calc, energetics_calc, elast_calc])
119
+ results = prop_calc.calc(structure)
119
120
  ```
120
121
 
121
122
  Chaining can also be used with the `calc_many` method, with parallelization.
@@ -136,8 +137,8 @@ the `MatCalc-Benchmark`.
136
137
  For example, the following code can be used to run the ElasticityBenchmark on `TensorNet-MatPES-PBE-v2025.1-PES` UMLIP.
137
138
 
138
139
  ```python
139
- from matcalc import PESCalculator
140
- calculator = PESCalculator.load_universal("TensorNet-MatPES-PBE-v2025.1-PES")
140
+ from matcalc import load_up
141
+ calculator = load_up("TensorNet-MatPES-PBE-v2025.1-PES")
141
142
  from matcalc.benchmark import ElasticityBenchmark
142
143
  benchmark = ElasticityBenchmark(fmax=0.05, relax_structure=True)
143
144
  results = benchmark.run(calculator, "TensorNet-MatPES")
@@ -42,9 +42,9 @@ dependencies = [
42
42
  "pymatgen",
43
43
  "numpy<2.0.0",
44
44
  "fsspec",
45
- "scikit-learn>=1.6.1",
45
+ "scikit-learn>=1.6.1"
46
46
  ]
47
- version = "0.2.0"
47
+ version = "0.2.2"
48
48
 
49
49
  [project.optional-dependencies]
50
50
  phonon = ["seekpath"]
@@ -67,6 +67,12 @@ grace = [
67
67
  matgl = [
68
68
  "matgl>=1.2.3",
69
69
  ]
70
+ orb = [
71
+ "orb-models>=0.4.2",
72
+ ]
73
+ ace = [
74
+ "pyace>=0.1.1",
75
+ ]
70
76
 
71
77
  [project.scripts]
72
78
  matcalc = "matcalc.cli:main"
@@ -84,8 +90,10 @@ build-backend = "setuptools.build_meta"
84
90
 
85
91
  [dependency-groups]
86
92
  dev = [
93
+ "codecov-cli>=10.3.0",
87
94
  "coverage>=7.7.1",
88
95
  "coveralls>=4.0.1",
96
+ "ipython>=8.34.0",
89
97
  "jupyter>=1.1.1",
90
98
  "maml>=2025.2.25",
91
99
  "matgl>=1.2.3",
@@ -102,13 +110,21 @@ lint = [
102
110
  "ruff>=0.11.2",
103
111
  ]
104
112
  maintain = [
113
+ "docutils>=0.21.2",
105
114
  "invoke>=2.2.0",
106
115
  "sphinx>=8.1.3",
107
116
  "sphinx-markdown-builder>=0.6.8",
108
117
  ]
118
+ test-models = [
119
+ "mace-torch>=0.3.12",
120
+ "orb-models>=0.4.2",
121
+ "pynanoflann>=0.10.0",
122
+ "sevenn>=0.10.4",
123
+ "tensorpotential>=0.5.1",
124
+ ]
109
125
 
110
126
  [tool.ruff]
111
- target-version = "py39"
127
+ target-version = "py310"
112
128
  line-length = 120
113
129
  extend-include = ["*.ipynb"]
114
130
 
@@ -168,6 +184,7 @@ exclude_lines = [
168
184
  "except PackageNotFoundError:",
169
185
  "if 0:",
170
186
  "if TYPE_CHECKING:",
187
+ "if typing.TYPE_CHECKING:",
171
188
  "if __name__ == .__main__.:",
172
189
  "if self.debug:",
173
190
  "if settings.DEBUG",
@@ -9,7 +9,7 @@ try:
9
9
  except PackageNotFoundError:
10
10
  pass # package not installed
11
11
 
12
- from .base import PropCalc
12
+ from .base import ChainedCalc, PropCalc
13
13
  from .config import clear_cache
14
14
  from .elasticity import ElasticityCalc
15
15
  from .eos import EOSCalc
@@ -19,3 +19,6 @@ from .qha import QHACalc
19
19
  from .relaxation import RelaxCalc
20
20
  from .stability import EnergeticsCalc
21
21
  from .utils import UNIVERSAL_CALCULATORS, PESCalculator
22
+
23
+ # Provide an alias for loading calculators quickly.
24
+ load_up = PESCalculator.load_universal
@@ -78,3 +78,66 @@ class PropCalc(abc.ABC):
78
78
  else:
79
79
  _func = self.calc # type: ignore[assignment]
80
80
  return parallel(delayed(_func)(s) for s in structures)
81
+
82
+
83
+ class ChainedCalc(PropCalc):
84
+ """A chained calculator that runs a series of PropCalcs on a structure or set of structures.
85
+
86
+ Often, you may want to obtain multiple properties at once, e.g., perform a relaxation with a formation energy
87
+ computation and a elasticity calculation. This can be done using this class by supplying a list of calculators.
88
+ Note that it is likely
89
+ """
90
+
91
+ def __init__(self, prop_calcs: Sequence[PropCalc]) -> None:
92
+ """
93
+ Initialize a chained calculator.
94
+
95
+ Args:
96
+ prop_calcs: Sequence of prop calcs.
97
+ """
98
+ self.prop_calcs = tuple(prop_calcs)
99
+
100
+ def calc(self, structure: Structure | dict[str, Any]) -> dict[str, Any]:
101
+ """
102
+ Runs the series of PropCalcs on a structure.
103
+
104
+ Args:
105
+ structure: Pymatgen structure or a dict containing a pymatgen Structure under a "final_structure" or
106
+ "structure" key. Allowing dicts provide the means to chain calculators, e.g., do a relaxation followed
107
+ by an elasticity calculation.
108
+
109
+ Returns:
110
+ dict[str, Any]: In the form {"prop_name": value}.
111
+ """
112
+ results = structure # type:ignore[assignment]
113
+ for prop_calc in self.prop_calcs:
114
+ results = prop_calc.calc(results) # type:ignore[assignment]
115
+ return results # type:ignore[return-value]
116
+
117
+ def calc_many(
118
+ self,
119
+ structures: Sequence[Structure | dict[str, Any]],
120
+ n_jobs: None | int = None,
121
+ allow_errors: bool = False, # noqa: FBT001,FBT002
122
+ **kwargs: Any,
123
+ ) -> Generator[dict | None]:
124
+ """Runs the sequence of PropCalc on many structures.
125
+
126
+ Args:
127
+ structures: List or generator of Structures.
128
+ n_jobs: The maximum number of concurrently running jobs. If -1 all CPUs are used. For n_jobs below -1,
129
+ (n_cpus + 1 + n_jobs) are used. None is a marker for `unset` that will be interpreted as n_jobs=1
130
+ unless the call is performed under a parallel_config() context manager that sets another value for
131
+ n_jobs.
132
+ allow_errors: Whether to skip failed calculations. For these calculations, None will be returned. For
133
+ large scale calculations, you may want this to be True to avoid the entire calculation failing.
134
+ Defaults to False.
135
+ **kwargs: Passthrough to calc_many method of all PropCalcs.
136
+
137
+ Returns:
138
+ Generator of dicts.
139
+ """
140
+ results = structures # type:ignore[assignment]
141
+ for prop_calc in self.prop_calcs:
142
+ results = prop_calc.calc_many(results, n_jobs=n_jobs, allow_errors=allow_errors, **kwargs) # type:ignore[assignment]
143
+ return results # type:ignore[return-value]
@@ -7,20 +7,24 @@ import json
7
7
  import logging
8
8
  import os
9
9
  import random
10
- import typing
11
10
  from pathlib import Path
11
+ from typing import TYPE_CHECKING, Any
12
12
  from urllib.parse import urlparse
13
13
 
14
14
  import fsspec
15
15
  import numpy as np
16
16
  import pandas as pd
17
17
  import requests
18
+ from matminer.featurizers.site import CrystalNNFingerprint
19
+ from matminer.featurizers.structure import SiteStatsFingerprint
18
20
  from monty.json import MontyDecoder
19
21
  from monty.serialization import dumpfn, loadfn
20
22
  from pymatgen.io.ase import AseAtomsAdaptor
21
23
  from scipy.optimize import curve_fit
22
24
 
23
- if typing.TYPE_CHECKING:
25
+ if TYPE_CHECKING:
26
+ from collections.abc import Sequence
27
+
24
28
  from ase.calculators.calculator import Calculator
25
29
 
26
30
  from .base import PropCalc
@@ -28,7 +32,7 @@ if typing.TYPE_CHECKING:
28
32
  from .config import BENCHMARK_DATA_DIR, BENCHMARK_DATA_DOWNLOAD_URL, BENCHMARK_DATA_URL
29
33
  from .elasticity import ElasticityCalc
30
34
  from .phonon import PhononCalc
31
- from .relaxation import RelaxCalc
35
+ from .stability import EnergeticsCalc
32
36
  from .units import eVA3ToGPa
33
37
 
34
38
  logger = logging.getLogger(__name__)
@@ -119,7 +123,7 @@ class CheckpointFile:
119
123
  return results, *[a[len(results) :] for a in args]
120
124
  return [], *args
121
125
 
122
- def save(self, results: list[dict[str, typing.Any]]) -> None:
126
+ def save(self, results: list[dict[str, Any]]) -> None:
123
127
  """
124
128
  Saves a list of results at the specified checkpoint location.
125
129
 
@@ -161,7 +165,7 @@ class Benchmark(metaclass=abc.ABCMeta):
161
165
  def __init__(
162
166
  self,
163
167
  benchmark_name: str | Path,
164
- properties: typing.Sequence[str],
168
+ properties: Sequence[str],
165
169
  index_name: str,
166
170
  other_fields: tuple = (),
167
171
  property_rename_map: dict[str, str] | None = None,
@@ -237,7 +241,7 @@ class Benchmark(metaclass=abc.ABCMeta):
237
241
  self.ground_truth = rows
238
242
 
239
243
  @abc.abstractmethod
240
- def get_prop_calc(self, calculator: Calculator, **kwargs: typing.Any) -> PropCalc:
244
+ def get_prop_calc(self, calculator: Calculator, **kwargs: Any) -> PropCalc:
241
245
  """
242
246
  Abstract method to retrieve a property calculation object using the provided calculator and additional
243
247
  parameters.
@@ -336,8 +340,7 @@ class Benchmark(metaclass=abc.ABCMeta):
336
340
  # We make sure of the generator from prop_calc.calc_many to do this in a memory efficient manner.
337
341
  # allow_errors typically should be true since some of the calculations may fail.
338
342
  for row, d in zip(
339
- ground_truth,
340
- prop_calc.calc_many(structures, n_jobs=n_jobs, allow_errors=True, **kwargs),
343
+ ground_truth, prop_calc.calc_many(structures, n_jobs=n_jobs, allow_errors=True, **kwargs), strict=True
341
344
  ):
342
345
  r = dict(row)
343
346
  r.update(self.process_result(d, model_name))
@@ -366,15 +369,16 @@ class Benchmark(metaclass=abc.ABCMeta):
366
369
  return results_df
367
370
 
368
371
 
369
- class RelaxationBenchmark(Benchmark):
372
+ class EquilibriumBenchmark(Benchmark):
370
373
  """
371
- Represents a benchmark for evaluating and analyzing relaxation properties of materials.
374
+ Represents a benchmark for evaluating and analyzing equilibrium properties of materials.
372
375
  This benchmark utilizes a dataset and provides functionality for property calculation
373
376
  and result processing. The class is designed to work with a predefined framework for
374
- benchmarking relaxation properties. The benchmark dataset contains data such as relaxed
375
- structures along with additional metadata. This class supports configurability through
376
- metadata files, index names, and additional benchmark properties. It relies on external
377
- calculators and utility classes for property computations and result handling.
377
+ benchmarking equilibrium properties. The benchmark dataset contains data such as relaxed
378
+ structures, un-/corrected formation energy along with additional metadata. This class
379
+ supports configurability through metadata files, index names, and additional benchmark
380
+ properties. It relies on external calculators and utility classes for property computations
381
+ and result handling.
378
382
  """
379
383
 
380
384
  def __init__(
@@ -385,9 +389,9 @@ class RelaxationBenchmark(Benchmark):
385
389
  **kwargs, # noqa:ANN003
386
390
  ) -> None:
387
391
  """
388
- Initializes the RelaxationBenchmark instance with specified benchmark metadata and
392
+ Initializes the EquilibriumBenchmark instance with specified benchmark metadata and
389
393
  configuration parameters. It sets up the benchmark with the necessary properties
390
- required for relaxation analysis.
394
+ required for equilibrium benchmark analysis.
391
395
 
392
396
  :param index_name: The name of the index used to uniquely identify records in the dataset.
393
397
  :type index_name: str
@@ -399,36 +403,38 @@ class RelaxationBenchmark(Benchmark):
399
403
  :type kwargs: dict
400
404
  """
401
405
  self.folder_name = folder_name
402
- # Define the expected property from the relaxation calculator.
403
- kwargs.setdefault("properties", ("structure",))
404
- # Other fields such as the material formula may be included.
406
+ kwargs.setdefault("properties", ("structure", "formation_energy_per_atom"))
407
+ kwargs.setdefault("property_rename_map", {"formation_energy_per_atom": "Eform"})
405
408
  kwargs.setdefault("other_fields", ("formula",))
406
409
  super().__init__(benchmark_name, index_name=index_name, **kwargs)
407
410
 
408
- def get_prop_calc(self, calculator: Calculator, **kwargs: typing.Any) -> PropCalc:
411
+ def get_prop_calc(self, calculator: Calculator, **kwargs: Any) -> PropCalc:
409
412
  """
410
- Returns a property calculation object for performing relaxation calculations.
411
- This method initializes the relaxation calculator using the provided Calculator
412
- object and any additional configuration parameters.
413
+ Returns a property calculation object for performing relaxation and formation energy
414
+ calculations. This method initializes the stability calculator using the provided
415
+ Calculator object and any additional configuration parameters.
413
416
 
414
- :param calculator: A Calculator object responsible for performing the relaxation calculation.
417
+ :param calculator: A Calculator object responsible for performing the relaxation and
418
+ formation energy calculation.
415
419
  :type calculator: Calculator
416
420
  :param kwargs: Additional keyword arguments used for configuration.
417
421
  :type kwargs: dict
418
- :return: An initialized PropCalc object configured for relaxation calculations.
422
+ :return: An initialized PropCalc object configured for relaxation and formation energy
423
+ calculations.
419
424
  :rtype: PropCalc
420
425
  """
421
- return RelaxCalc(calculator, **kwargs)
426
+ return EnergeticsCalc(calculator, relax_calc_kwargs={"perturb_distance": 0.1}, **kwargs)
422
427
 
423
428
  def process_result(self, result: dict | None, model_name: str) -> dict:
424
429
  """
425
- Processes the result dictionary containing final relaxed structures, formats the keys
426
- according to the provided model name. If the result is None, default values of
427
- NaN are returned for final structures.
430
+ Processes the result dictionary containing final structures and formation energy per atom,
431
+ formats the keys according to the provided model name. If the result is None, default values
432
+ of NaN are returned for final structures or formation energy per atom.
428
433
 
429
434
  :param result:
430
- A dictionary containing the final relaxed structures under the keys
431
- 'final_structure'. It can also be None to indicate missing elemental_refs.
435
+ A dictionary containing the final structures and formation energy per atom under the keys
436
+ 'final_structure' and 'formation energy per atom'. It can also be None to indicate missing
437
+ elemental_refs.
432
438
  :type result: dict or None
433
439
 
434
440
  :param model_name:
@@ -437,15 +443,97 @@ class RelaxationBenchmark(Benchmark):
437
443
  :type model_name: str
438
444
 
439
445
  :return:
440
- A dictionary containing the specific final relaxed structure prefixed by the model name.
441
- The values will be NaN if the input result is None.
446
+ A dictionary containing the specific final structure and formation energy per atomprefixed
447
+ by the model name. The values will be NaN if the input result is None.
442
448
 
443
449
  :rtype: dict
444
450
  """
445
451
  return {
446
452
  f"structure_{model_name}": (result["final_structure"] if result is not None else float("nan")),
453
+ f"formation_energy_per_atom_{model_name}": (
454
+ result["formation_energy_per_atom"] if result is not None else float("nan")
455
+ ),
447
456
  }
448
457
 
458
+ def run(
459
+ self,
460
+ calculator: Calculator,
461
+ model_name: str,
462
+ *,
463
+ n_jobs: None | int = -1,
464
+ checkpoint_file: str | Path | None = None,
465
+ checkpoint_freq: int = 1000,
466
+ delete_checkpoint_on_finish: bool = True,
467
+ include_full_results: bool = False,
468
+ **kwargs, # noqa:ANN003
469
+ ) -> pd.DataFrame:
470
+ """
471
+ Processes a collection of structures using a calculator, saves intermittent checkpoints,
472
+ and returns the results in a DataFrame. In addition to the base processing performed
473
+ by the parent class, this method computes the Euclidean distance between the relaxed
474
+ structure (obtained from the property calculation) and the reference DFT structure,
475
+ using SiteStatsFingerprint. The computed distance is added as a new column in the
476
+ results DataFrame with the key "distance_{model_name}".
477
+
478
+ This function supports parallel computation and allows for error tolerance during processing.
479
+ It retrieves a property calculator and utilizes it to calculate desired results for the given
480
+ set of structures. Checkpoints are saved periodically based on the specified frequency,
481
+ ensuring that progress is not lost in case of interruptions.
482
+
483
+ :param calculator: ASE-compatible calculator instance used to provide PES information for PropCalc.
484
+ :type calculator: Calculator
485
+ :param model_name: Name of the model used for properties' calculation.
486
+ This name is updated in the results DataFrame.
487
+ :type model_name: str
488
+ :param n_jobs: Number of parallel jobs to be used in the computation. Use -1
489
+ to allocate all cores available on the system. Defaults to -1.
490
+ :type n_jobs: int | None
491
+ :param checkpoint_file: File path where checkpoint elemental_refs is saved periodically.
492
+ If None, no checkpoints are saved.
493
+ :type checkpoint_file: str | Path | None
494
+ :param checkpoint_freq: Frequency after which checkpoint elemental_refs is saved.
495
+ Corresponds to the number of structures processed.
496
+ :type checkpoint_freq: int
497
+ :param delete_checkpoint_on_finish: Whether to delete checkpoint files when the benchmark finishes. Defaults to
498
+ True.
499
+ :type delete_checkpoint_on_finish: bool
500
+ :param include_full_results: Whether to save full results from PropCalc.calc for analysis afterwards. For
501
+ instance, the ElasticityProp does not just compute the bulk and shear moduli, but also the full elastic
502
+ tensors, which can be used for other kinds of analysis. Defaults to False.
503
+ :type include_full_results: bool
504
+ :param kwargs: Additional keyword arguments passed to the property calculator,
505
+ for instance, to customize its behavior or computation options.
506
+ :type kwargs: dict
507
+ :return: A pandas DataFrame containing the processed results for the given
508
+ input structures. The DataFrame includes updated results and relevant
509
+ metrics.
510
+ :rtype: pd.DataFrame
511
+ """
512
+ results_df = super().run(
513
+ calculator,
514
+ model_name,
515
+ n_jobs=n_jobs,
516
+ checkpoint_file=checkpoint_file,
517
+ checkpoint_freq=checkpoint_freq,
518
+ delete_checkpoint_on_finish=delete_checkpoint_on_finish,
519
+ include_full_results=include_full_results,
520
+ **kwargs,
521
+ )
522
+
523
+ ssf = SiteStatsFingerprint(
524
+ CrystalNNFingerprint.from_preset("ops", distance_cutoffs=None, x_diff_weight=0),
525
+ stats=("mean", "std_dev", "minimum", "maximum"),
526
+ )
527
+
528
+ results_df[f"d_{model_name}"] = [
529
+ np.linalg.norm(np.array(ssf.featurize(model)) - np.array(ssf.featurize(dft)))
530
+ if model is not None and dft is not None
531
+ else np.nan
532
+ for model, dft in zip(results_df[f"structure_{model_name}"], results_df["structure_DFT"], strict=True)
533
+ ]
534
+
535
+ return results_df
536
+
449
537
 
450
538
  class ElasticityBenchmark(Benchmark):
451
539
  """
@@ -490,7 +578,7 @@ class ElasticityBenchmark(Benchmark):
490
578
  **kwargs,
491
579
  )
492
580
 
493
- def get_prop_calc(self, calculator: Calculator, **kwargs: typing.Any) -> PropCalc:
581
+ def get_prop_calc(self, calculator: Calculator, **kwargs: Any) -> PropCalc:
494
582
  """
495
583
  Calculates and returns a property calculation object based on the provided
496
584
  calculator and optional parameters. This is useful for initializing and
@@ -581,7 +669,7 @@ class PhononBenchmark(Benchmark):
581
669
  **kwargs,
582
670
  )
583
671
 
584
- def get_prop_calc(self, calculator: Calculator, **kwargs: typing.Any) -> PropCalc:
672
+ def get_prop_calc(self, calculator: Calculator, **kwargs: Any) -> PropCalc:
585
673
  """
586
674
  Retrieves a phonon calculation instance based on the given calculator and
587
675
  additional keyword arguments.
@@ -598,7 +686,7 @@ class PhononBenchmark(Benchmark):
598
686
  optional parameters.
599
687
  :rtype: PropCalc
600
688
  """
601
- return PhononCalc(calculator, **kwargs)
689
+ return PhononCalc(calculator, write_phonon=False, **kwargs)
602
690
 
603
691
  def process_result(self, result: dict | None, model_name: str) -> dict:
604
692
  """
@@ -23,7 +23,7 @@ def calculate_property(args: Any) -> None:
23
23
  :param args:
24
24
  :return:
25
25
  """
26
- calculator = mtc.PESCalculator.load_universal(args.model)
26
+ calculator = mtc.load_up(args.model)
27
27
  mod = mtc.__dict__[args.property](calculator)
28
28
  results = []
29
29
  for f in args.structure:
@@ -11,7 +11,7 @@ BENCHMARK_DATA_DIR = pathlib.Path.home() / ".cache" / "matcalc"
11
11
 
12
12
 
13
13
  def clear_cache(*, confirm: bool = True) -> None:
14
- """Deletes all files in the matgl.cache. This is used to clean out downloaded models.
14
+ """Deletes all files in the mattcalc cache. This is used to clean out downloaded benchmarks.
15
15
 
16
16
  Args:
17
17
  confirm: Whether to ask for confirmation. Default is True.
@@ -30,6 +30,7 @@ class ElasticityCalc(PropCalc):
30
30
  norm_strains: Sequence[float] | float = (-0.01, -0.005, 0.005, 0.01),
31
31
  shear_strains: Sequence[float] | float = (-0.06, -0.03, 0.03, 0.06),
32
32
  fmax: float = 0.1,
33
+ symmetry: bool = False,
33
34
  relax_structure: bool = True,
34
35
  relax_deformed_structures: bool = False,
35
36
  use_equilibrium: bool = True,
@@ -43,10 +44,11 @@ class ElasticityCalc(PropCalc):
43
44
  shear_strains: single or multiple strain values to apply to each shear mode.
44
45
  Defaults to (-0.06, -0.03, 0.03, 0.06).
45
46
  fmax: maximum force in the relaxed structure (if relax_structure). Defaults to 0.1.
47
+ symmetry: whether or not to use symmetry reduction. Defaults to False.
46
48
  relax_structure: whether to relax the provided structure with the given calculator.
47
49
  Defaults to True.
48
50
  relax_deformed_structures: whether to relax the atomic positions of the deformed/strained structures
49
- with the given calculator. Defaults to True.
51
+ with the given calculator. Defaults to False.
50
52
  use_equilibrium: whether to use the equilibrium stress and strain. Ignored and set
51
53
  to True if either norm_strains or shear_strains has length 1 or is a float.
52
54
  Defaults to True.
@@ -65,6 +67,7 @@ class ElasticityCalc(PropCalc):
65
67
  self.relax_structure = relax_structure
66
68
  self.relax_deformed_structures = relax_deformed_structures
67
69
  self.fmax = fmax
70
+ self.symmetry = symmetry
68
71
  if len(self.norm_strains) > 1 and len(self.shear_strains) > 1:
69
72
  self.use_equilibrium = use_equilibrium
70
73
  else:
@@ -93,14 +96,17 @@ class ElasticityCalc(PropCalc):
93
96
 
94
97
  if self.relax_structure or self.relax_deformed_structures:
95
98
  relax_calc = RelaxCalc(self.calculator, fmax=self.fmax, **(self.relax_calc_kwargs or {}))
96
- result |= relax_calc.calc(structure_in)
97
- structure_in = result["final_structure"]
98
- relax_calc.relax_cell = False
99
+ if self.relax_structure:
100
+ result |= relax_calc.calc(structure_in)
101
+ structure_in = result["final_structure"]
102
+ if self.relax_deformed_structures:
103
+ relax_calc.relax_cell = False
99
104
 
100
105
  deformed_structure_set = DeformedStructureSet(
101
106
  structure_in,
102
107
  self.norm_strains,
103
108
  self.shear_strains,
109
+ self.symmetry,
104
110
  )
105
111
  stresses = []
106
112
  for deformed_structure in deformed_structure_set: