ase-calculator-kit 0.3.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 (41) hide show
  1. ase_calculator_kit-0.3.2/LICENSE +21 -0
  2. ase_calculator_kit-0.3.2/PKG-INFO +502 -0
  3. ase_calculator_kit-0.3.2/README.md +451 -0
  4. ase_calculator_kit-0.3.2/pyproject.toml +75 -0
  5. ase_calculator_kit-0.3.2/setup.cfg +4 -0
  6. ase_calculator_kit-0.3.2/src/ase_calculator_kit/__init__.py +50 -0
  7. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/__init__.py +24 -0
  8. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/base.py +25 -0
  9. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/dft/__init__.py +11 -0
  10. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/dft/espresso.py +56 -0
  11. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/dft/vasp.py +46 -0
  12. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/mlip/__init__.py +17 -0
  13. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/mlip/chgnet.py +73 -0
  14. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/mlip/fairchem.py +95 -0
  15. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/mlip/mattersim.py +86 -0
  16. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/mlip/nequip.py +110 -0
  17. ase_calculator_kit-0.3.2/src/ase_calculator_kit/backends/mlip/sevennet.py +153 -0
  18. ase_calculator_kit-0.3.2/src/ase_calculator_kit/config.py +98 -0
  19. ase_calculator_kit-0.3.2/src/ase_calculator_kit/device.py +63 -0
  20. ase_calculator_kit-0.3.2/src/ase_calculator_kit/dispersion.py +193 -0
  21. ase_calculator_kit-0.3.2/src/ase_calculator_kit/errors.py +39 -0
  22. ase_calculator_kit-0.3.2/src/ase_calculator_kit/factory.py +113 -0
  23. ase_calculator_kit-0.3.2/src/ase_calculator_kit/py.typed +0 -0
  24. ase_calculator_kit-0.3.2/src/ase_calculator_kit/registry.py +37 -0
  25. ase_calculator_kit-0.3.2/src/ase_calculator_kit.egg-info/PKG-INFO +502 -0
  26. ase_calculator_kit-0.3.2/src/ase_calculator_kit.egg-info/SOURCES.txt +39 -0
  27. ase_calculator_kit-0.3.2/src/ase_calculator_kit.egg-info/dependency_links.txt +1 -0
  28. ase_calculator_kit-0.3.2/src/ase_calculator_kit.egg-info/requires.txt +33 -0
  29. ase_calculator_kit-0.3.2/src/ase_calculator_kit.egg-info/top_level.txt +1 -0
  30. ase_calculator_kit-0.3.2/tests/test_config.py +96 -0
  31. ase_calculator_kit-0.3.2/tests/test_device.py +66 -0
  32. ase_calculator_kit-0.3.2/tests/test_dft_backends.py +114 -0
  33. ase_calculator_kit-0.3.2/tests/test_dft_factory.py +47 -0
  34. ase_calculator_kit-0.3.2/tests/test_dispersion.py +136 -0
  35. ase_calculator_kit-0.3.2/tests/test_factory.py +99 -0
  36. ase_calculator_kit-0.3.2/tests/test_fairchem_backend.py +14 -0
  37. ase_calculator_kit-0.3.2/tests/test_mattersim_backend.py +28 -0
  38. ase_calculator_kit-0.3.2/tests/test_nequip_backend.py +85 -0
  39. ase_calculator_kit-0.3.2/tests/test_packaging.py +68 -0
  40. ase_calculator_kit-0.3.2/tests/test_sevennet_backend.py +76 -0
  41. ase_calculator_kit-0.3.2/tests/test_singlepoint_cpu.py +127 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ishikawa-group
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,502 @@
1
+ Metadata-Version: 2.4
2
+ Name: ase-calculator-kit
3
+ Version: 0.3.2
4
+ Summary: Unified ASE calculator factory for MLIP and DFT calculators.
5
+ Author: ishikawa-group
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ishikawa-group/ase-calculator-kit
8
+ Project-URL: Repository, https://github.com/ishikawa-group/ase-calculator-kit
9
+ Project-URL: Changelog, https://github.com/ishikawa-group/ase-calculator-kit/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/ishikawa-group/ase-calculator-kit/issues
11
+ Keywords: ase,calculator,dft,machine-learning,interatomic-potential,mlip,chemistry,materials
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Intended Audience :: Science/Research
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
20
+ Classifier: Topic :: Scientific/Engineering :: Physics
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: ase<4,>=3.28
26
+ Requires-Dist: pyyaml<7,>=6.0
27
+ Provides-Extra: chgnet
28
+ Requires-Dist: chgnet<0.5,>=0.4; extra == "chgnet"
29
+ Provides-Extra: sevennet
30
+ Requires-Dist: sevenn<0.13,>=0.12; extra == "sevennet"
31
+ Provides-Extra: mattersim
32
+ Requires-Dist: mattersim<2,>=1.2; extra == "mattersim"
33
+ Provides-Extra: nequip
34
+ Requires-Dist: nequip<0.19,>=0.18; extra == "nequip"
35
+ Provides-Extra: uma
36
+ Requires-Dist: fairchem-core<3,>=2.20; extra == "uma"
37
+ Provides-Extra: dispersion
38
+ Requires-Dist: torch-dftd<0.6,>=0.5; extra == "dispersion"
39
+ Provides-Extra: all
40
+ Requires-Dist: chgnet<0.5,>=0.4; extra == "all"
41
+ Requires-Dist: sevenn<0.13,>=0.12; extra == "all"
42
+ Requires-Dist: mattersim<2,>=1.2; extra == "all"
43
+ Requires-Dist: nequip<0.19,>=0.18; extra == "all"
44
+ Requires-Dist: fairchem-core<3,>=2.20; extra == "all"
45
+ Requires-Dist: torch-dftd<0.6,>=0.5; extra == "all"
46
+ Provides-Extra: dev
47
+ Requires-Dist: pytest==9.0.3; extra == "dev"
48
+ Requires-Dist: ruff==0.15.15; extra == "dev"
49
+ Requires-Dist: tqdm==4.67.3; extra == "dev"
50
+ Dynamic: license-file
51
+
52
+ # ase-calculator-kit
53
+
54
+ A thin, unified [ASE](https://wiki.fysik.dtu.dk/ase/) calculator factory for
55
+ machine-learning interatomic potentials and external DFT calculators. Every call
56
+ returns a standard `ase.Calculator`, so the rest of your ASE workflow stays
57
+ unchanged.
58
+
59
+ Supported MLIP backends:
60
+
61
+ - [SevenNet](https://github.com/MDIL-SNU/SevenNet) (installed by default)
62
+ - [CHGNet](https://github.com/CederGroupHub/chgnet)
63
+ - [MatterSim](https://github.com/microsoft/mattersim)
64
+ - [NequIP OAM](https://www.nequip.net/)
65
+ - [UMA / fairchem](https://github.com/facebookresearch/fairchem)
66
+
67
+ Supported DFT backends:
68
+
69
+ - VASP
70
+ - Quantum ESPRESSO (`qe`, `espresso`, `quantum-espresso`)
71
+
72
+ ## Install
73
+
74
+ ```bash
75
+ pip install ase-calculator-kit
76
+ ```
77
+
78
+ The default installation is intentionally lightweight: it pulls in ASE and
79
+ PyYAML and **no NNP backend**, so it does not drag in torch. Each backend is an
80
+ explicit extra — install only what your workflow needs:
81
+
82
+ ```bash
83
+ # One backend
84
+ pip install "ase-calculator-kit[sevennet]"
85
+ pip install "ase-calculator-kit[chgnet]"
86
+ pip install "ase-calculator-kit[mattersim]"
87
+ pip install "ase-calculator-kit[nequip]"
88
+ pip install "ase-calculator-kit[uma]"
89
+
90
+ # Several selected backends
91
+ pip install "ase-calculator-kit[chgnet,mattersim]"
92
+
93
+ # Every supported NNP backend and the optional D3 correction
94
+ pip install "ase-calculator-kit[all]"
95
+
96
+ # D3 correction without installing every NNP backend
97
+ pip install "ase-calculator-kit[dispersion]"
98
+ ```
99
+
100
+ Missing backend packages are reported only when that calculator is requested,
101
+ with the matching extra to install.
102
+
103
+ ### Python versions
104
+
105
+ Python 3.12 and newer. The package itself has no upper bound, but one backend
106
+ currently does:
107
+
108
+ | | 3.12 | 3.13 | 3.14 |
109
+ |---|:--:|:--:|:--:|
110
+ | Core, `chgnet`, `sevennet`, `mattersim`, `nequip`, `dispersion` | ✅ | ✅ | ✅ |
111
+ | `uma` (and therefore `all`) | ✅ | ✅ | ❌ |
112
+
113
+ `fairchem-core` declares `requires-python = ">=3.11,<3.14"` and pins
114
+ `torch~=2.8.0`, which has no cp314 wheels, so `pip install
115
+ "ase-calculator-kit[uma]"` fails on Python 3.14 with an error naming
116
+ `fairchem-core`. Nothing here needs to change once fairchem-core supports 3.14.
117
+
118
+ This is deliberately *not* hidden behind an environment marker: a marker would
119
+ make the install succeed on 3.14 while silently leaving UMA out.
120
+
121
+ Use this import for new code:
122
+
123
+ ```python
124
+ from ase_calculator_kit import get_calculator
125
+ ```
126
+
127
+ ## Usage
128
+
129
+ MLIP calculators keep the lightweight keyword API:
130
+
131
+ ```python
132
+ from ase.build import bulk
133
+ from ase_calculator_kit import get_calculator
134
+
135
+ atoms = bulk("Cu", "fcc", a=3.6)
136
+
137
+ atoms.calc = get_calculator("sevennet", model="7net-omni", modal="mpa")
138
+ print(atoms.get_potential_energy())
139
+
140
+ atoms.calc = get_calculator("chgnet", device="mps")
141
+ print(atoms.get_potential_energy())
142
+
143
+ atoms.calc = get_calculator("mattersim", model="5M")
144
+ print(atoms.get_potential_energy())
145
+
146
+ atoms.calc = get_calculator("nequip", model="L")
147
+ print(atoms.get_potential_energy())
148
+
149
+ atoms.calc = get_calculator("uma", model="uma-s-1p2", task="omat")
150
+ print(atoms.get_potential_energy())
151
+ ```
152
+
153
+ DFT calculators are config-only:
154
+
155
+ ```python
156
+ from ase_calculator_kit import get_calculator
157
+
158
+ atoms.calc = get_calculator("vasp", config="examples/dft/vasp_pbe_static.yaml")
159
+ atoms.calc = get_calculator("qe", config="examples/dft/qe_pbe_static.yaml")
160
+ ```
161
+
162
+ For VASP and QE, arbitrary keyword arguments are intentionally rejected to keep
163
+ calculation conditions explicit and reproducible:
164
+
165
+ ```python
166
+ get_calculator("vasp", encut=520) # TypeError
167
+ ```
168
+
169
+ For reproducibility, VASP configs must explicitly specify `profile.command`
170
+ (QE additionally requires `profile.pseudo_dir` and `pseudopotentials`);
171
+ environment-variable-only execution is intentionally not used by this wrapper.
172
+
173
+ Use `overrides=` for small dynamic changes:
174
+
175
+ ```python
176
+ atoms.calc = get_calculator(
177
+ "vasp",
178
+ config="examples/dft/vasp_pbe_static.yaml",
179
+ overrides={"directory": "runs/vasp/Cu_001"},
180
+ )
181
+ ```
182
+
183
+ Write the final merged config for auditability:
184
+
185
+ ```python
186
+ atoms.calc = get_calculator(
187
+ "qe",
188
+ config="examples/dft/qe_pbe_static.yaml",
189
+ overrides={"directory": "runs/qe/Cu_001"},
190
+ write_resolved_config=True,
191
+ )
192
+ ```
193
+
194
+ ## API Reference
195
+
196
+ ### Calculator names
197
+
198
+ `get_calculator(name, **kwargs)` takes one of these names (case-insensitive);
199
+ `available_calculators()` returns the same list at runtime.
200
+
201
+ | `name` | Kind | Aliases |
202
+ |---|---|---|
203
+ | `sevennet` | MLIP | — |
204
+ | `chgnet` | MLIP | — |
205
+ | `mattersim` | MLIP | — |
206
+ | `nequip` | MLIP | — |
207
+ | `uma` | MLIP | `fairchem` |
208
+ | `vasp` | DFT | — |
209
+ | `qe` | DFT | `espresso`, `quantum-espresso` |
210
+
211
+ An unknown name raises `ValueError` listing the valid names.
212
+
213
+ ### MLIP keyword arguments
214
+
215
+ All MLIP backends accept `device=` (`"auto"` by default; see
216
+ [Apple Silicon (MPS) support](#apple-silicon-mps-support)), `dispersion=False`,
217
+ `dispersion_xc=None`, and forward any extra keywords to the underlying
218
+ calculator.
219
+
220
+ | `name` | Backend-specific keywords (defaults) |
221
+ |---|---|
222
+ | `sevennet` | `model="7net-omni"`, `modal="mpa"`, `enable_cueq=False`, `enable_flash=False` |
223
+ | `chgnet` | `model=None` (bundled default), `checkpoint=None` (path to a `.pth`) |
224
+ | `mattersim` | `model="1M"` (or `"5M"`), `load_path=None` |
225
+ | `nequip` | `model="L"` (`S`/`M`/`L`/`XL`), `model_path=None`, `compile_mode="eager"`, `neighborlist_backend="matscipy"`, `allow_tf32=False` |
226
+ | `uma` | `model="uma-s-1p2"`, `task="omat"` |
227
+
228
+ ### DFT keyword arguments
229
+
230
+ DFT backends accept **only** these three; anything else raises `TypeError`.
231
+
232
+ | Keyword | Default | Meaning |
233
+ |---|---|---|
234
+ | `config` | *required* | YAML path or `dict` of calculation conditions |
235
+ | `overrides` | `None` | `dict` deep-merged over `config` |
236
+ | `write_resolved_config` | `False` | Write the merged config into the run directory |
237
+
238
+ ### Public helpers
239
+
240
+ ```python
241
+ from ase_calculator_kit import (
242
+ attach_calculator,
243
+ available_calculators,
244
+ available_dft_calculators,
245
+ available_mlip_models,
246
+ available_models,
247
+ get_dft_calculator,
248
+ get_mlip_calculator,
249
+ resolve_calculator_config,
250
+ )
251
+
252
+ available_mlip_models() # ['chgnet', 'fairchem', 'mattersim', 'nequip', 'sevennet', 'uma']
253
+ available_dft_calculators() # ['espresso', 'qe', 'quantum-espresso', 'vasp']
254
+ available_calculators() # both of the above; available_models() is an alias
255
+ attach_calculator(atoms, "uma", task="omat") # sets atoms.calc, returns atoms
256
+ ```
257
+
258
+ ### Exceptions
259
+
260
+ ```python
261
+ from ase_calculator_kit import CalculatorKitError, DispersionError, MissingDependencyError
262
+ ```
263
+
264
+ | Exception | Also a | Raised when |
265
+ |---|---|---|
266
+ | `CalculatorKitError` | `Exception` | Base class for everything below |
267
+ | `MissingDependencyError` | `ImportError` | The backend package is not installed; the message names the extra to install |
268
+ | `DispersionError` | `ValueError` | `dispersion=True` is not allowed for that model (see [Dispersion](#dispersion)) |
269
+ | `ValueError` | — | Unknown calculator name, unsupported `device`, or an incomplete DFT config |
270
+ | `TypeError` | — | A DFT backend was given a keyword other than the three above, or `config=` was omitted |
271
+
272
+ ## Examples
273
+
274
+ Run a CPU single point with every MLIP model/variant:
275
+
276
+ ```bash
277
+ python examples/run_all_models.py
278
+ python examples/run_all_models.py --device auto
279
+ python examples/run_all_models.py --only chgnet sevennet nequip
280
+ ```
281
+
282
+ Create DFT calculator objects from YAML without running VASP/QE:
283
+
284
+ ```bash
285
+ python examples/dft/create_dft_calculator_from_config.py vasp \
286
+ examples/dft/vasp_pbe_static.yaml
287
+ ```
288
+
289
+ DFT YAML examples live in [`examples/dft`](examples/dft).
290
+
291
+ ## Apple Silicon (MPS) support
292
+
293
+ Every MLIP backend was run on a single point (`bulk("Cu")`) with `device="mps"`
294
+ on an Apple Silicon Mac (arm64, PyTorch 2.8, MPS available). Results:
295
+
296
+ | Backend | `device="mps"` | Notes |
297
+ |---|---|---|
298
+ | SevenNet | ✅ supported | validated locally (`7net-omni`) |
299
+ | CHGNet | ✅ supported | validated locally |
300
+ | MatterSim | ✅ supported | validated locally |
301
+ | NequIP OAM | ❌ not supported | PyTorch MPS lacks float64; the packaged OAM models use float64 buffers |
302
+ | UMA / fairchem | ❌ not supported | `fairchem-core` asserts `device in {"cpu", "cuda"}` |
303
+
304
+ For the MPS-supported backends, `device="auto"` resolves to `mps` on Apple
305
+ Silicon when no CUDA device is present. NequIP and UMA accept only `"cpu"` /
306
+ `"cuda"`; passing `device="mps"` raises a clear `ValueError`, and `device="auto"`
307
+ falls back to `cpu`.
308
+
309
+ ## Choosing an MLIP Variant
310
+
311
+ ### SevenNet `modal`
312
+
313
+ | `modal` | Use for | Reference level |
314
+ |---|---|---|
315
+ | `mpa` (default) | General-purpose, including molecules | PBE(+U) |
316
+ | `omat24` | Broad / high-force configurations | PBE(+U) |
317
+ | `matpes_pbe` | PBE without Hubbard U | PBE |
318
+ | `matpes_r2scan` | r2SCAN-level materials | r2SCAN |
319
+ | `mp_r2scan` | r2SCAN-level Materials Project data | r2SCAN |
320
+ | `oc20` | Catalyst surfaces and adsorption | RPBE |
321
+ | `oc22` | Oxide catalysis | PBE(+U) |
322
+ | `odac23` | MOFs / direct air capture | PBE-D3 |
323
+ | `omol25_low` | **Low-spin** molecular systems | ωB97M-V |
324
+ | `omol25_high` | **High-spin** molecular systems only | ωB97M-V |
325
+ | `spice` | Drug-like molecules and peptides | ωB97M-D3(BJ) |
326
+ | `qcml` | Small molecules, wide element coverage | PBE0 + MBD-NL |
327
+ | `pet_mad` | PBEsol-level data | PBEsol |
328
+
329
+ `omol25_low` and `omol25_high` split OMol25 by **spin state**, not by accuracy —
330
+ pick the one matching your system. SevenNet's own guidance is that `mpa` stays
331
+ the recommended default even for molecules, organic crystals, and molecular
332
+ liquids; choose another task only when you need consistency with a specific
333
+ functional or benchmark protocol.
334
+
335
+ Single-fidelity models such as `7net-0` do not take `modal`; pass `modal=None`.
336
+
337
+ ### NequIP OAM `model`
338
+
339
+ | `model` | Use for |
340
+ |---|---|
341
+ | `S` | Smallest OAM model for quick checks |
342
+ | `M` | Medium OAM model |
343
+ | `L` (default) | Recommended general OAM model for inorganic solids |
344
+ | `XL` | Largest OAM model when higher capacity is worth the cost |
345
+
346
+ NequIP OAM models are loaded through NequIP's `nequip.net:` loader and cached by
347
+ NequIP. To avoid a download, pass `model_path="path/to/model.nequip.zip"`.
348
+
349
+ ### MatterSim `model`
350
+
351
+ `1M` (default) is for fast screening, `5M` is more accurate. Keep the checkpoint
352
+ fixed across a campaign.
353
+
354
+ ### UMA `task`
355
+
356
+ | `task` | Use for |
357
+ |---|---|
358
+ | `omat` (default) | Inorganic bulk/materials, stress, cell optimization |
359
+ | `omol` | Molecules and polymers |
360
+ | `oc20` | Catalyst surfaces and adsorption |
361
+ | `oc22` | Oxide catalysis |
362
+ | `oc25` | Electrochemistry / solid-liquid interfaces |
363
+ | `odac` | MOFs and direct air capture |
364
+ | `omc` | Molecular crystals |
365
+
366
+ For the molecular task (`omol`), set `atoms.info["charge"]` and
367
+ `atoms.info["spin"]` before computing — see
368
+ [Molecular systems](#molecular-systems-charge-and-spin) for why this matters.
369
+
370
+ ## Molecular systems (charge and spin)
371
+
372
+ Molecular models need two inputs that no bulk model does: the **total charge**
373
+ of the system and its **spin multiplicity** (`2S+1`). ASE has no standard place
374
+ for either, so they are passed through `atoms.info`, and the backends differ in
375
+ whether they read them at all.
376
+
377
+ | Backend | Molecular option | Takes charge / spin? |
378
+ |---|---|---|
379
+ | `uma` | `task="omol"` | ✅ `atoms.info["charge"]`, `atoms.info["spin"]` |
380
+ | `sevennet` | `modal="omol25_low"` / `"omol25_high"` / `"spice"` / `"qcml"` | ❌ not supported by sevenn |
381
+
382
+ ### UMA: set both keys explicitly
383
+
384
+ ```python
385
+ from ase.build import molecule
386
+ from ase_calculator_kit import get_calculator
387
+
388
+ atoms = molecule("H2O")
389
+ atoms.info["charge"] = 0 # total charge
390
+ atoms.info["spin"] = 1 # spin multiplicity, 2S+1 (1 = closed shell)
391
+ atoms.calc = get_calculator("uma", task="omol")
392
+ print(atoms.get_potential_energy())
393
+ ```
394
+
395
+ A hydroxide anion and a neutral radical are the cases that actually bite:
396
+
397
+ ```python
398
+ oh_minus = molecule("OH")
399
+ oh_minus.info["charge"] = -1 # anion
400
+ oh_minus.info["spin"] = 1 # closed shell
401
+ oh_minus.calc = get_calculator("uma", task="omol")
402
+
403
+ oh_radical = molecule("OH")
404
+ oh_radical.info["charge"] = 0
405
+ oh_radical.info["spin"] = 2 # doublet — one unpaired electron
406
+ oh_radical.calc = get_calculator("uma", task="omol")
407
+ ```
408
+
409
+ > **Do not rely on the defaults.** fairchem does *not* raise when `charge` or
410
+ > `spin` is missing. It logs a warning, writes `charge=0` / `spin=1` into the
411
+ > `atoms.info` dict you passed in, and returns a neutral closed-shell result.
412
+ > An ion or an open-shell species then comes back **silently wrong**. Set both
413
+ > keys on every molecular structure, including the ones you think are obvious.
414
+
415
+ Both keys are integers. `charge` may range from -100 to 100 and `spin` from 0 to
416
+ 100; they are read only by the `omol` head, and other UMA tasks ignore them.
417
+
418
+ ### SevenNet: no charge or spin input
419
+
420
+ sevenn has no charge or spin argument, so the `modal` embedding is the only
421
+ handle on the molecular reference data. Charged species and a chosen open-shell
422
+ state **cannot be expressed** — `omol25_high` selects a model trained on
423
+ high-spin configurations, but it is not a multiplicity you set per structure.
424
+ Use `get_calculator("uma", task="omol")` when the charge and spin of the system
425
+ matter.
426
+
427
+ ### Non-periodic cells
428
+
429
+ `ase.build.molecule()` returns `pbc=False` with a zero cell, which UMA accepts.
430
+ UMA rejects only two ambiguous cases: a fully periodic structure whose cell is
431
+ all zeros, and a partially periodic one (`pbc=[True, True, False]`).
432
+
433
+ ## Dispersion
434
+
435
+ Add a Grimme-D3(BJ) correction on top of MLIP models with `dispersion=True`:
436
+
437
+ ```python
438
+ atoms.calc = get_calculator("uma", task="omat", dispersion=True)
439
+ atoms.calc = get_calculator("uma", task="oc20", dispersion=True)
440
+ atoms.calc = get_calculator("chgnet", dispersion=True)
441
+ atoms.calc = get_calculator("sevennet", modal="pet_mad", dispersion=True)
442
+ ```
443
+
444
+ With `dispersion=True` the returned object is an ASE
445
+ `SumCalculator([backend_calculator, d3_calculator])`, not the backend calculator
446
+ itself — it satisfies the same `ase.Calculator` interface, but do not rely on
447
+ backend-specific attributes or `isinstance` checks against the backend class.
448
+
449
+ Some models already include dispersion in their training functional, so
450
+ `dispersion=True` is refused for them with `DispersionError`:
451
+
452
+ ```python
453
+ get_calculator("uma", task="omol", dispersion=True) # DispersionError: ωB97M-V includes VV10
454
+ get_calculator("sevennet", modal="spice", dispersion=True) # DispersionError: SPICE is ωB97M-D3(BJ)
455
+ ```
456
+
457
+ **Every molecular task falls in this category** — molecular reference data is
458
+ almost always dispersion-corrected, each dataset in its own way (VV10, an
459
+ explicit D3(BJ) term, or MBD-NL). That verdict cannot be overridden with
460
+ `dispersion_xc=`; remove `dispersion=True` instead. A task this table does not
461
+ cover yet is refused by default but *can* be unlocked with an explicit
462
+ `dispersion_xc` once you have checked its functional yourself.
463
+
464
+ See [`docs/models.md`](docs/models.md) for the full per-model table.
465
+
466
+ ## Why no MACE?
467
+
468
+ MACE is intentionally excluded. `mace-torch` requires an `e3nn` version that
469
+ conflicts with the `e3nn` pinned by SevenNet (`sevenn`) and UMA
470
+ (`fairchem-core`). If you need MACE, use dedicated MACE tooling in a separate
471
+ environment.
472
+
473
+ ## Development
474
+
475
+ ```bash
476
+ python -m venv .venv
477
+ .venv/bin/pip install -e ".[dev]" -c constraints.txt
478
+ .venv/bin/pytest
479
+ ```
480
+
481
+ `pyproject.toml` declares compatible version ranges so the package installs
482
+ next to whatever ASE/NNP versions you already have; `constraints.txt` pins the
483
+ exact combination that is tested, and CI installs with it.
484
+
485
+ `pytest` runs only the fast tests by default. Slow tests
486
+ (`pytest -m slow`) run real MLIP CPU single-point calculations and may download
487
+ model weights; install `.[dev,all] -c constraints.txt` first so every backend is
488
+ importable.
489
+
490
+ ## Further Reading
491
+
492
+ - [`docs/models.md`](docs/models.md) — per-model dispersion policy and training
493
+ functionals.
494
+ - [`docs/code-guide_ja.md`](docs/code-guide_ja.md) — 実装の化学的な判断と
495
+ モジュールの責務(日本語).
496
+ - [`AGENTS.md`](AGENTS.md) — repository map, invariants, and conventions for
497
+ AI coding agents (Claude, GPT, and others).
498
+ - [`CHANGELOG.md`](CHANGELOG.md) — release history.
499
+
500
+ ## License
501
+
502
+ MIT