matcalc 0.2.1__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.1/src/matcalc.egg-info → matcalc-0.2.2}/PKG-INFO +16 -12
  2. {matcalc-0.2.1 → matcalc-0.2.2}/README.md +11 -11
  3. {matcalc-0.2.1 → matcalc-0.2.2}/pyproject.toml +17 -2
  4. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/__init__.py +3 -0
  5. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/cli.py +1 -1
  6. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/utils.py +5 -62
  7. {matcalc-0.2.1 → matcalc-0.2.2/src/matcalc.egg-info}/PKG-INFO +16 -12
  8. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc.egg-info/requires.txt +6 -0
  9. matcalc-0.2.2/tests/test_utils.py +168 -0
  10. matcalc-0.2.1/tests/test_utils.py +0 -157
  11. {matcalc-0.2.1 → matcalc-0.2.2}/LICENSE +0 -0
  12. {matcalc-0.2.1 → matcalc-0.2.2}/setup.cfg +0 -0
  13. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/base.py +0 -0
  14. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/benchmark.py +0 -0
  15. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/config.py +0 -0
  16. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/elasticity.py +0 -0
  17. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/elemental_refs/MatPES-PBE-Element-Refs.json.gz +0 -0
  18. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/elemental_refs/MatPES-r2SCAN-Element-Refs.json.gz +0 -0
  19. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/eos.py +0 -0
  20. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/neb.py +0 -0
  21. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/phonon.py +0 -0
  22. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/py.typed +0 -0
  23. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/qha.py +0 -0
  24. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/relaxation.py +0 -0
  25. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/stability.py +0 -0
  26. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc/units.py +0 -0
  27. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc.egg-info/SOURCES.txt +0 -0
  28. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc.egg-info/dependency_links.txt +0 -0
  29. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc.egg-info/entry_points.txt +0 -0
  30. {matcalc-0.2.1 → matcalc-0.2.2}/src/matcalc.egg-info/top_level.txt +0 -0
  31. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_benchmark.py +0 -0
  32. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_chain.py +0 -0
  33. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_cli.py +0 -0
  34. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_elasticity.py +0 -0
  35. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_eos.py +0 -0
  36. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_neb.py +0 -0
  37. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_phonon.py +0 -0
  38. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_qha.py +0 -0
  39. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_relaxation.py +0 -0
  40. {matcalc-0.2.1 → matcalc-0.2.2}/tests/test_stability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matcalc
3
- Version: 0.2.1
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, ChainedCalc
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",
@@ -182,8 +186,8 @@ the `MatCalc-Benchmark`.
182
186
  For example, the following code can be used to run the ElasticityBenchmark on `TensorNet-MatPES-PBE-v2025.1-PES` UMLIP.
183
187
 
184
188
  ```python
185
- from matcalc import PESCalculator
186
- 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")
187
191
  from matcalc.benchmark import ElasticityBenchmark
188
192
  benchmark = ElasticityBenchmark(fmax=0.05, relax_structure=True)
189
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, ChainedCalc
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",
@@ -137,8 +137,8 @@ the `MatCalc-Benchmark`.
137
137
  For example, the following code can be used to run the ElasticityBenchmark on `TensorNet-MatPES-PBE-v2025.1-PES` UMLIP.
138
138
 
139
139
  ```python
140
- from matcalc import PESCalculator
141
- 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")
142
142
  from matcalc.benchmark import ElasticityBenchmark
143
143
  benchmark = ElasticityBenchmark(fmax=0.05, relax_structure=True)
144
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.1"
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"
@@ -87,6 +93,7 @@ dev = [
87
93
  "codecov-cli>=10.3.0",
88
94
  "coverage>=7.7.1",
89
95
  "coveralls>=4.0.1",
96
+ "ipython>=8.34.0",
90
97
  "jupyter>=1.1.1",
91
98
  "maml>=2025.2.25",
92
99
  "matgl>=1.2.3",
@@ -103,10 +110,18 @@ lint = [
103
110
  "ruff>=0.11.2",
104
111
  ]
105
112
  maintain = [
113
+ "docutils>=0.21.2",
106
114
  "invoke>=2.2.0",
107
115
  "sphinx>=8.1.3",
108
116
  "sphinx-markdown-builder>=0.6.8",
109
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
+ ]
110
125
 
111
126
  [tool.ruff]
112
127
  target-version = "py310"
@@ -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
@@ -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:
@@ -286,31 +286,26 @@ class PESCalculator(Calculator):
286
286
  result = name
287
287
 
288
288
  elif any(name.lower().startswith(m) for m in ("m3gnet", "chgnet", "tensornet", "pbe", "r2scan")):
289
- import matgl
290
- from matgl.ext.ase import PESCalculator as PESCalculator_
291
-
292
289
  name = MODEL_ALIASES.get(name.lower(), name)
293
- model = matgl.load_model(name) # type: ignore[arg-type]
294
- kwargs.setdefault("stress_unit", "eV/A3")
295
- result = PESCalculator_(potential=model, **kwargs)
290
+ result = PESCalculator.load_matgl(name, **kwargs)
296
291
 
297
292
  elif name.lower() == "mace": # pragma: no cover
298
293
  from mace.calculators import mace_mp
299
294
 
300
295
  result = mace_mp(**kwargs)
301
296
 
302
- elif name.lower() == "sevennet": # pragma: no cover
297
+ elif name.lower() == "sevennet":
303
298
  from sevenn.calculator import SevenNetCalculator
304
299
 
305
300
  result = SevenNetCalculator(**kwargs)
306
301
 
307
- elif name.lower() == "grace" or name.lower() == "tensorpotential": # pragma: no cover
302
+ elif name.lower() == "grace" or name.lower() == "tensorpotential":
308
303
  from tensorpotential.calculator.foundation_models import grace_fm
309
304
 
310
305
  kwargs.setdefault("model", "GRACE-2L-OAM")
311
306
  result = grace_fm(**kwargs)
312
307
 
313
- elif name.lower() == "orb": # pragma: no cover
308
+ elif name.lower() == "orb":
314
309
  from orb_models.forcefield.calculator import ORBCalculator
315
310
  from orb_models.forcefield.pretrained import ORB_PRETRAINED_MODELS
316
311
 
@@ -344,59 +339,7 @@ def get_universal_calculator(name: str | Calculator, **kwargs: Any) -> Calculato
344
339
  Returns:
345
340
  Calculator
346
341
  """
347
- result: Calculator
348
-
349
- if not isinstance(name, str): # e.g. already an ase Calculator instance
350
- result = name
351
-
352
- elif name.lower().startswith("m3gnet") or name.lower().startswith("tensornet"):
353
- import matgl
354
- from matgl.ext.ase import PESCalculator as PESCalculator_
355
-
356
- # M3GNet is shorthand for latest M3GNet based on DIRECT sampling.
357
- # TensorNet is shorthand for latest TensorNet trained on MatPES.
358
- name = {"m3gnet": "M3GNet-MP-2021.2.8-DIRECT-PES", "tensornet": "TensorNet-MatPES-PBE-v2025.1-PES"}.get(
359
- name.lower(), name
360
- )
361
- model = matgl.load_model(name) # type: ignore[arg-type]
362
- kwargs.setdefault("stress_unit", "eV/A3")
363
- result = PESCalculator_(potential=model, **kwargs)
364
-
365
- elif name.lower() == "chgnet":
366
- from chgnet.model.dynamics import CHGNetCalculator
367
-
368
- result = CHGNetCalculator(**kwargs)
369
-
370
- elif name.lower() == "mace":
371
- from mace.calculators import mace_mp
372
-
373
- result = mace_mp(**kwargs)
374
-
375
- elif name.lower() == "sevennet":
376
- from sevenn.calculator import SevenNetCalculator
377
-
378
- result = SevenNetCalculator(**kwargs)
379
-
380
- elif name.lower() == "grace" or name.lower() == "tensorpotential":
381
- from tensorpotential.calculator.foundation_models import grace_fm
382
-
383
- kwargs.setdefault("model", "GRACE-2L-OAM")
384
- result = grace_fm(**kwargs)
385
-
386
- elif name.lower() == "orb":
387
- from orb_models.forcefield.calculator import ORBCalculator
388
- from orb_models.forcefield.pretrained import ORB_PRETRAINED_MODELS
389
-
390
- model = kwargs.pop("model", "orb-v2")
391
- device = kwargs.get("device", "cpu")
392
-
393
- orbff = ORB_PRETRAINED_MODELS[model](device=device)
394
- result = ORBCalculator(orbff, **kwargs)
395
-
396
- else:
397
- raise ValueError(f"Unrecognized {name=}, must be one of {UNIVERSAL_CALCULATORS}")
398
-
399
- return result
342
+ return PESCalculator.load_universal(name=name, **kwargs)
400
343
 
401
344
 
402
345
  def is_ase_optimizer(key: str | Optimizer) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matcalc
3
- Version: 0.2.1
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, ChainedCalc
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",
@@ -182,8 +186,8 @@ the `MatCalc-Benchmark`.
182
186
  For example, the following code can be used to run the ElasticityBenchmark on `TensorNet-MatPES-PBE-v2025.1-PES` UMLIP.
183
187
 
184
188
  ```python
185
- from matcalc import PESCalculator
186
- 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")
187
191
  from matcalc.benchmark import ElasticityBenchmark
188
192
  benchmark = ElasticityBenchmark(fmax=0.05, relax_structure=True)
189
193
  results = benchmark.run(calculator, "TensorNet-MatPES")
@@ -6,6 +6,9 @@ numpy<2.0.0
6
6
  fsspec
7
7
  scikit-learn>=1.6.1
8
8
 
9
+ [ace]
10
+ pyace>=0.1.1
11
+
9
12
  [benchmark]
10
13
  matminer>=0.9.3
11
14
 
@@ -24,6 +27,9 @@ matgl>=1.2.3
24
27
  [nequip]
25
28
  nequip>=0.6.2
26
29
 
30
+ [orb]
31
+ orb-models>=0.4.2
32
+
27
33
  [phonon]
28
34
  seekpath
29
35
 
@@ -0,0 +1,168 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib.util import find_spec
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ import ase.optimize
8
+ import pytest
9
+ from ase import Atoms
10
+ from ase.calculators.calculator import Calculator
11
+ from ase.optimize.optimize import Optimizer
12
+
13
+ from matcalc import RelaxCalc
14
+ from matcalc.utils import (
15
+ MODEL_ALIASES,
16
+ UNIVERSAL_CALCULATORS,
17
+ VALID_OPTIMIZERS,
18
+ PESCalculator,
19
+ get_ase_optimizer,
20
+ get_universal_calculator,
21
+ is_ase_optimizer,
22
+ )
23
+
24
+ DIR = Path(__file__).parent.absolute()
25
+
26
+ if TYPE_CHECKING:
27
+ from pymatgen.core import Structure
28
+
29
+
30
+ @pytest.mark.parametrize(
31
+ ("expected_unit", "expected_weight"),
32
+ [
33
+ ("eV/A3", 0.006241509125883258),
34
+ ("GPa", 1.0),
35
+ ],
36
+ )
37
+ @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
38
+ def test_pescalculator_load_mtp(expected_unit: str, expected_weight: float) -> None:
39
+ calc = PESCalculator.load_mtp(
40
+ filename=DIR / "pes" / "MTP-Cu-2020.1-PES" / "fitted.mtp",
41
+ elements=["Cu"],
42
+ )
43
+ assert isinstance(calc, Calculator)
44
+ assert PESCalculator(
45
+ potential=calc.potential,
46
+ stress_unit=expected_unit,
47
+ ).stress_weight == pytest.approx(expected_weight)
48
+ with pytest.raises(ValueError, match="Unsupported stress_unit: Pa. Must be 'GPa' or 'eV/A3'."):
49
+ PESCalculator(potential=calc.potential, stress_unit="Pa")
50
+
51
+
52
+ @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
53
+ def test_pescalculator_load_gap() -> None:
54
+ calc = PESCalculator.load_gap(filename=DIR / "pes" / "GAP-NiMo-2020.3-PES" / "gap.xml")
55
+ assert isinstance(calc, Calculator)
56
+
57
+
58
+ @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
59
+ def test_pescalculator_load_nnp() -> None:
60
+ calc = PESCalculator.load_nnp(
61
+ input_filename=DIR / "pes" / "NNP-Cu-2020.1-PES" / "input.nn",
62
+ scaling_filename=DIR / "pes" / "NNP-Cu-2020.1-PES" / "scaling.data",
63
+ weights_filenames=[DIR / "pes" / "NNP-Cu-2020.1-PES" / "weights.029.data"],
64
+ )
65
+ assert isinstance(calc, Calculator)
66
+
67
+
68
+ @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
69
+ @pytest.mark.skipif(not find_spec("lammps"), reason="lammps is not installed")
70
+ def test_pescalculator_load_snap() -> None:
71
+ for name in ("SNAP", "qSNAP"):
72
+ calc = PESCalculator.load_snap(
73
+ param_file=DIR / "pes" / f"{name}-Cu-2020.1-PES" / "SNAPotential.snapparam",
74
+ coeff_file=DIR / "pes" / f"{name}-Cu-2020.1-PES" / "SNAPotential.snapcoeff",
75
+ )
76
+ assert isinstance(calc, Calculator)
77
+
78
+
79
+ @pytest.mark.skipif(not find_spec("pyace"), reason="pyace is not installed")
80
+ def test_pescalculator_load_ace() -> None:
81
+ calc = PESCalculator.load_ace(basis_set=DIR / "pes" / "ACE-Cu-2021.5.15-PES" / "Cu-III.yaml")
82
+ assert isinstance(calc, Calculator)
83
+
84
+
85
+ @pytest.mark.skipif(not find_spec("nequip"), reason="nequip is not installed")
86
+ def test_pescalculator_load_nequip() -> None:
87
+ calc = PESCalculator.load_nequip(model_path=DIR / "pes" / "NequIP-am-Al2O3-2023.9-PES" / "default.pth")
88
+ assert isinstance(calc, Calculator)
89
+
90
+
91
+ @pytest.mark.skipif(not find_spec("matgl"), reason="matgl is not installed")
92
+ def test_pescalculator_load_matgl() -> None:
93
+ calc = PESCalculator.load_matgl(path=DIR / "pes" / "M3GNet-MP-2021.2.8-PES")
94
+ assert isinstance(calc, Calculator)
95
+
96
+
97
+ @pytest.mark.skipif(not find_spec("matgl"), reason="matgl is not installed")
98
+ @pytest.mark.skipif(not find_spec("mace"), reason="mace is not installed")
99
+ @pytest.mark.skipif(not find_spec("sevenn"), reason="sevenn is not installed")
100
+ def test_pescalculator_load_universal(Li2O: Structure) -> None:
101
+ for name in UNIVERSAL_CALCULATORS:
102
+ calc = PESCalculator.load_universal(name)
103
+ assert isinstance(calc, Calculator)
104
+ same_calc = PESCalculator.load_universal(calc) # test ASE Calculator classes are returned as-is
105
+ assert calc is same_calc
106
+ # We run a basic relaxation calc to make sure that all calculators work.
107
+ relaxed_calc = RelaxCalc(calc, relax_cell=False, relax_atoms=False)
108
+ result = relaxed_calc.calc(Li2O)
109
+ assert isinstance(result, dict)
110
+
111
+ name = "whatever"
112
+ with pytest.raises(ValueError, match=f"Unrecognized {name=}") as exc:
113
+ get_universal_calculator(name)
114
+ assert str(exc.value) == f"Unrecognized {name=}, must be one of {UNIVERSAL_CALCULATORS}"
115
+
116
+
117
+ @pytest.mark.skipif(not find_spec("lammps"), reason="lammps is not installed")
118
+ def test_pescalculator_calculate() -> None:
119
+ calc = PESCalculator.load_snap(
120
+ param_file=DIR / "pes" / "SNAP-Cu-2020.1-PES" / "SNAPotential.snapparam",
121
+ coeff_file=DIR / "pes" / "SNAP-Cu-2020.1-PES" / "SNAPotential.snapcoeff",
122
+ )
123
+
124
+ atoms = Atoms(
125
+ "Cu4",
126
+ scaled_positions=[(0, 0, 0), (0, 0.5, 0.5), (0.5, 0, 0.5), (0.5, 0.5, 0)],
127
+ cell=[3.57743067] * 3,
128
+ pbc=True,
129
+ )
130
+
131
+ atoms.set_calculator(calc)
132
+
133
+ energy = atoms.get_potential_energy()
134
+ forces = atoms.get_forces()
135
+ stresses = atoms.get_stress()
136
+
137
+ assert isinstance(energy, float)
138
+ assert list(forces.shape) == [4, 3]
139
+ assert list(stresses.shape) == [6]
140
+
141
+
142
+ def test_get_ase_optimizer() -> None:
143
+ for name in dir(ase.optimize):
144
+ if is_ase_optimizer(name):
145
+ optimizer = get_ase_optimizer(name)
146
+ assert issubclass(optimizer, Optimizer)
147
+ same_optimizer = get_ase_optimizer(optimizer) # test ASE Optimizer classes are returned as-is
148
+ assert optimizer is same_optimizer
149
+
150
+ for optimizer in ("whatever", 42):
151
+ with pytest.raises(ValueError, match=f"Unknown {optimizer=}") as exc:
152
+ get_ase_optimizer(optimizer)
153
+ assert str(exc.value) == f"Unknown {optimizer=}, must be one of {VALID_OPTIMIZERS}"
154
+
155
+
156
+ def test_is_ase_optimizer() -> None:
157
+ assert is_ase_optimizer(ase.optimize.BFGS)
158
+ assert is_ase_optimizer(Optimizer)
159
+ assert not is_ase_optimizer(Calculator)
160
+
161
+ for name in ("whatever", 42, -3.14):
162
+ assert not is_ase_optimizer(name)
163
+
164
+
165
+ def test_aliases() -> None:
166
+ # Ensures that model aliases always point to valid models.
167
+ for v in MODEL_ALIASES.values():
168
+ assert v in UNIVERSAL_CALCULATORS
@@ -1,157 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from importlib.util import find_spec
4
- from pathlib import Path
5
-
6
- import ase.optimize
7
- import pytest
8
- from ase import Atoms
9
- from ase.calculators.calculator import Calculator
10
- from ase.optimize.optimize import Optimizer
11
-
12
- from matcalc.utils import (
13
- MODEL_ALIASES,
14
- UNIVERSAL_CALCULATORS,
15
- VALID_OPTIMIZERS,
16
- PESCalculator,
17
- get_ase_optimizer,
18
- get_universal_calculator,
19
- is_ase_optimizer,
20
- )
21
-
22
- DIR = Path(__file__).parent.absolute()
23
-
24
-
25
- class TestPESCalculator:
26
- @pytest.mark.parametrize(
27
- ("expected_unit", "expected_weight"),
28
- [
29
- ("eV/A3", 0.006241509125883258),
30
- ("GPa", 1.0),
31
- ],
32
- )
33
- @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
34
- def test_pescalculator_load_mtp(self, expected_unit: str, expected_weight: float) -> None:
35
- calc = PESCalculator.load_mtp(
36
- filename=DIR / "pes" / "MTP-Cu-2020.1-PES" / "fitted.mtp",
37
- elements=["Cu"],
38
- )
39
- assert isinstance(calc, Calculator)
40
- assert PESCalculator(
41
- potential=calc.potential,
42
- stress_unit=expected_unit,
43
- ).stress_weight == pytest.approx(expected_weight)
44
- with pytest.raises(ValueError, match="Unsupported stress_unit: Pa. Must be 'GPa' or 'eV/A3'."):
45
- PESCalculator(potential=calc.potential, stress_unit="Pa")
46
-
47
- @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
48
- def test_pescalculator_load_gap(self) -> None:
49
- calc = PESCalculator.load_gap(filename=DIR / "pes" / "GAP-NiMo-2020.3-PES" / "gap.xml")
50
- assert isinstance(calc, Calculator)
51
-
52
- @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
53
- def test_pescalculator_load_nnp(self) -> None:
54
- calc = PESCalculator.load_nnp(
55
- input_filename=DIR / "pes" / "NNP-Cu-2020.1-PES" / "input.nn",
56
- scaling_filename=DIR / "pes" / "NNP-Cu-2020.1-PES" / "scaling.data",
57
- weights_filenames=[DIR / "pes" / "NNP-Cu-2020.1-PES" / "weights.029.data"],
58
- )
59
- assert isinstance(calc, Calculator)
60
-
61
- @pytest.mark.skipif(not find_spec("maml"), reason="maml is not installed")
62
- @pytest.mark.skipif(not find_spec("lammps"), reason="lammps is not installed")
63
- def test_pescalculator_load_snap(self) -> None:
64
- for name in ("SNAP", "qSNAP"):
65
- calc = PESCalculator.load_snap(
66
- param_file=DIR / "pes" / f"{name}-Cu-2020.1-PES" / "SNAPotential.snapparam",
67
- coeff_file=DIR / "pes" / f"{name}-Cu-2020.1-PES" / "SNAPotential.snapcoeff",
68
- )
69
- assert isinstance(calc, Calculator)
70
-
71
- @pytest.mark.skipif(not find_spec("pyace"), reason="pyace is not installed")
72
- def test_pescalculator_load_ace(self) -> None:
73
- calc = PESCalculator.load_ace(basis_set=DIR / "pes" / "ACE-Cu-2021.5.15-PES" / "Cu-III.yaml")
74
- assert isinstance(calc, Calculator)
75
-
76
- @pytest.mark.skipif(not find_spec("nequip"), reason="nequip is not installed")
77
- def test_pescalculator_load_nequip(self) -> None:
78
- calc = PESCalculator.load_nequip(model_path=DIR / "pes" / "NequIP-am-Al2O3-2023.9-PES" / "default.pth")
79
- assert isinstance(calc, Calculator)
80
-
81
- @pytest.mark.skipif(not find_spec("matgl"), reason="matgl is not installed")
82
- def test_pescalculator_load_matgl(self) -> None:
83
- calc = PESCalculator.load_matgl(path=DIR / "pes" / "M3GNet-MP-2021.2.8-PES")
84
- assert isinstance(calc, Calculator)
85
-
86
- @pytest.mark.skipif(not find_spec("matgl"), reason="matgl is not installed")
87
- @pytest.mark.skipif(not find_spec("chgnet"), reason="chgnet is not installed")
88
- @pytest.mark.skipif(not find_spec("mace"), reason="mace is not installed")
89
- @pytest.mark.skipif(not find_spec("sevenn"), reason="sevenn is not installed")
90
- def test_pescalculator_load_universal(self) -> None:
91
- for name in UNIVERSAL_CALCULATORS:
92
- calc = PESCalculator.load_universal(name)
93
- assert isinstance(calc, Calculator)
94
- same_calc = PESCalculator.load_universal(calc) # test ASE Calculator classes are returned as-is
95
- assert calc is same_calc
96
-
97
- name = "whatever"
98
- with pytest.raises(ValueError, match=f"Unrecognized {name=}") as exc:
99
- get_universal_calculator(name)
100
- assert str(exc.value) == f"Unrecognized {name=}, must be one of {UNIVERSAL_CALCULATORS}"
101
-
102
- # cover edge case like https://github.com/materialsvirtuallab/matcalc/issues/14
103
- # where non-str and non-ASE Calculator instances are passed in
104
- assert get_universal_calculator(42) == 42 # test non-str input is returned as-is
105
-
106
- @pytest.mark.skipif(not find_spec("lammps"), reason="lammps is not installed")
107
- def test_pescalculator_calculate(self) -> None:
108
- calc = PESCalculator.load_snap(
109
- param_file=DIR / "pes" / "SNAP-Cu-2020.1-PES" / "SNAPotential.snapparam",
110
- coeff_file=DIR / "pes" / "SNAP-Cu-2020.1-PES" / "SNAPotential.snapcoeff",
111
- )
112
-
113
- atoms = Atoms(
114
- "Cu4",
115
- scaled_positions=[(0, 0, 0), (0, 0.5, 0.5), (0.5, 0, 0.5), (0.5, 0.5, 0)],
116
- cell=[3.57743067] * 3,
117
- pbc=True,
118
- )
119
-
120
- atoms.set_calculator(calc)
121
-
122
- energy = atoms.get_potential_energy()
123
- forces = atoms.get_forces()
124
- stresses = atoms.get_stress()
125
-
126
- assert isinstance(energy, float)
127
- assert list(forces.shape) == [4, 3]
128
- assert list(stresses.shape) == [6]
129
-
130
-
131
- def test_get_ase_optimizer() -> None:
132
- for name in dir(ase.optimize):
133
- if is_ase_optimizer(name):
134
- optimizer = get_ase_optimizer(name)
135
- assert issubclass(optimizer, Optimizer)
136
- same_optimizer = get_ase_optimizer(optimizer) # test ASE Optimizer classes are returned as-is
137
- assert optimizer is same_optimizer
138
-
139
- for optimizer in ("whatever", 42):
140
- with pytest.raises(ValueError, match=f"Unknown {optimizer=}") as exc:
141
- get_ase_optimizer(optimizer)
142
- assert str(exc.value) == f"Unknown {optimizer=}, must be one of {VALID_OPTIMIZERS}"
143
-
144
-
145
- def test_is_ase_optimizer() -> None:
146
- assert is_ase_optimizer(ase.optimize.BFGS)
147
- assert is_ase_optimizer(Optimizer)
148
- assert not is_ase_optimizer(Calculator)
149
-
150
- for name in ("whatever", 42, -3.14):
151
- assert not is_ase_optimizer(name)
152
-
153
-
154
- def test_aliases() -> None:
155
- # Ensures that model aliases always point to valid models.
156
- for v in MODEL_ALIASES.values():
157
- assert v in UNIVERSAL_CALCULATORS
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes