atst-tools 2.3.0__tar.gz → 2.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.
- atst_tools-2.3.2/PKG-INFO +184 -0
- atst_tools-2.3.2/README.md +147 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/pyproject.toml +2 -2
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/abacuslite_backend.py +1 -1
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/constant_potential.py +22 -4
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/factory.py +10 -2
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/config_schema.py +55 -7
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/md_post.py +71 -9
- atst_tools-2.3.2/src/atst_tools/workflows/cp_md.py +466 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/md.py +33 -4
- atst_tools-2.3.2/src/atst_tools.egg-info/PKG-INFO +184 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools.egg-info/SOURCES.txt +1 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools.egg-info/requires.txt +1 -1
- atst_tools-2.3.0/PKG-INFO +0 -462
- atst_tools-2.3.0/README.md +0 -425
- atst_tools-2.3.0/src/atst_tools.egg-info/PKG-INFO +0 -462
- {atst_tools-2.3.0 → atst_tools-2.3.2}/COPYING +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/LICENSE +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/setup.cfg +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/api/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/api/models.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/api/runner.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/api/services.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/api/validation.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/bench/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/bench/batch_runner.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/bench/batch_worker.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/bench/record.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/bench/sweep.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/backend_identity.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/base.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/calculators/dp.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/ABACUSLITE_SNAPSHOT.md +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/PATCHES.md +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/core.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/io/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/io/generalio.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/io/latestio.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/io/legacyio.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/utils/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/ASE_interface/abacuslite/utils/ksampling.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/LICENSE +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/UPSTREAM.md +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/dmf.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/fbenm.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/interpolate.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/torch/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/torch/_torch_config.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/torch/dmf.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/torch/fbenm.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/external/pydmf/dmf/torch/interpolate.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/mep/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/mep/autoneb.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/mep/ccqn.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/mep/dimer.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/mep/neb.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/mep/sella.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/cli_dispatch.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/counters.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/devices.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/errors.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/evidence.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/runtime/launch.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/scripts/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/scripts/cli.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/scripts/cli_impl.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/scripts/main.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/abacus_io.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/analysis.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/artifacts.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/banner.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/config.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/config_docs.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/convergence.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/dmf_validation.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/electrochemistry.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/gate_compensation.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/geometry.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/idpp.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/io.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/mpi.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/neb_endpoints.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/plot.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/post.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/reactive_modes.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/restart_helpers.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/reverse_config.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/sella_event_summary.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/sella_events.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/summary.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/thermochemistry.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/utils/ts_validation.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/__init__.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/constant_potential.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/d2s.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/dmf.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/execution.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/irc.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/relax.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools/workflows/vibration.py +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools.egg-info/dependency_links.txt +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools.egg-info/entry_points.txt +0 -0
- {atst_tools-2.3.0 → atst_tools-2.3.2}/src/atst_tools.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: atst-tools
|
|
3
|
+
Version: 2.3.2
|
|
4
|
+
Summary: Advanced ASE Transition State Tools for ABACUS and Deep-Potential
|
|
5
|
+
License-Expression: LGPL-3.0-or-later
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: COPYING
|
|
10
|
+
Requires-Dist: abacuslite<2,>=1.2.1
|
|
11
|
+
Requires-Dist: ase>=3.28.0
|
|
12
|
+
Requires-Dist: numpy<3,>=1.26
|
|
13
|
+
Requires-Dist: scipy<2,>=1.13
|
|
14
|
+
Requires-Dist: pydantic<3,>=2
|
|
15
|
+
Requires-Dist: ruamel.yaml<0.20,>=0.18
|
|
16
|
+
Requires-Dist: seekpath<3,>=2.2
|
|
17
|
+
Requires-Dist: sella<3,>=2.5
|
|
18
|
+
Provides-Extra: plot
|
|
19
|
+
Requires-Dist: matplotlib<4,>=3.9; extra == "plot"
|
|
20
|
+
Provides-Extra: dp
|
|
21
|
+
Requires-Dist: deepmd-kit>=3.1.3; extra == "dp"
|
|
22
|
+
Provides-Extra: parallel
|
|
23
|
+
Requires-Dist: mpi4py>=4.1.2; extra == "parallel"
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest<10,>=8.4; extra == "test"
|
|
26
|
+
Requires-Dist: tomli<3,>=2.0; extra == "test"
|
|
27
|
+
Provides-Extra: release
|
|
28
|
+
Requires-Dist: build<2,>=1.5; extra == "release"
|
|
29
|
+
Requires-Dist: twine<7,>=6.2; extra == "release"
|
|
30
|
+
Requires-Dist: tomli<3,>=2.0; extra == "release"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest<10,>=8.4; extra == "dev"
|
|
33
|
+
Requires-Dist: build<2,>=1.5; extra == "dev"
|
|
34
|
+
Requires-Dist: twine<7,>=6.2; extra == "dev"
|
|
35
|
+
Requires-Dist: tomli<3,>=2.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# ATST-Tools
|
|
39
|
+
|
|
40
|
+
[](pyproject.toml)
|
|
41
|
+
[](pyproject.toml)
|
|
42
|
+
[](#license)
|
|
43
|
+
|
|
44
|
+
ATST-Tools is an **ASE transition-state workflow toolkit** for ABACUS and
|
|
45
|
+
DeePMD-kit. Run calculations from YAML with the `atst` command, or use the
|
|
46
|
+
[Python API](docs/user/PYTHON_API_REFERENCE.md) to integrate the same workflows
|
|
47
|
+
into another program.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
atst run config.yaml
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Chinese guide: [中文 · GitHub](https://github.com/QuantumMisaka/atst-tools/blob/main/docs/user/USER_GUIDE_CN.md)
|
|
54
|
+
· [Gitee](https://gitee.com/jamesmisaka/atst-tools/blob/main/docs/user/USER_GUIDE_CN.md).
|
|
55
|
+
Examples: [GitHub](https://github.com/QuantumMisaka/atst-tools/tree/main/examples)
|
|
56
|
+
· [Gitee](https://gitee.com/jamesmisaka/atst-tools/tree/main/examples).
|
|
57
|
+
|
|
58
|
+
## What You Can Run
|
|
59
|
+
|
|
60
|
+
| Workflow | `calculation.type` |
|
|
61
|
+
| :--- | :--- |
|
|
62
|
+
| NEB / DyNEB and adaptive-image NEB | `neb`, `autoneb` |
|
|
63
|
+
| Dimer, Sella and CCQN saddle searches | `dimer`, `sella`, `ccqn` |
|
|
64
|
+
| Double-ended to single-ended TS search | `d2s` |
|
|
65
|
+
| Structure relaxation | `relax` |
|
|
66
|
+
| Vibrations and thermochemistry | `vibration` |
|
|
67
|
+
| Intrinsic reaction coordinates | `irc` |
|
|
68
|
+
| ASE-driven or ABACUS-native molecular dynamics | `md` |
|
|
69
|
+
| Experimental Direct MaxFlux path search | `dmf` |
|
|
70
|
+
| Experimental ABACUS constant-potential single points and serial scans | `constant_potential` |
|
|
71
|
+
|
|
72
|
+
Experimental constant-potential fixed-cell relaxation, NEB and ASE-driven MD
|
|
73
|
+
use the explicit `compensated_gate` boundary. See the
|
|
74
|
+
[configuration reference](docs/user/CONFIG_REFERENCE.md#211-constant-potential-evaluation-development-candidate)
|
|
75
|
+
for setup and supported boundaries.
|
|
76
|
+
|
|
77
|
+
Local commands prepare paths and ABACUS inputs, inspect trajectories, and
|
|
78
|
+
summarize existing results. See the [CLI reference](docs/user/CLI_REFERENCE.md).
|
|
79
|
+
A transition-state candidate can be screened with `atst vibration validate`
|
|
80
|
+
using saved vibration results; workflow completion alone does not establish a
|
|
81
|
+
validated TS. Current support and experimental limits are summarized in the
|
|
82
|
+
[feature matrix](docs/reports/FEATURE_STATUS_MATRIX.md).
|
|
83
|
+
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
Requires Python 3.10 or later:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install atst-tools
|
|
90
|
+
atst --version
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
| Use | Installation |
|
|
94
|
+
| :--- | :--- |
|
|
95
|
+
| Serial workflows, including serial NEB/AutoNEB | `pip install atst-tools` |
|
|
96
|
+
| DeePMD-kit calculator | `pip install "atst-tools[dp]"` |
|
|
97
|
+
| Image-parallel NEB/AutoNEB | `pip install "atst-tools[parallel]"` |
|
|
98
|
+
|
|
99
|
+
The default installation includes `abacuslite>=1.2.1,<2` and Sella; it does not
|
|
100
|
+
install `mpi4py`. ABACUS calculations also need an ABACUS executable and the
|
|
101
|
+
pseudopotential/orbital files referenced by the configuration. DP calculations
|
|
102
|
+
need a compatible model. Image-parallel runs require a matching MPI runtime;
|
|
103
|
+
DMF additionally requires `cyipopt` and IPOPT. See the
|
|
104
|
+
[MPI setup guide](docs/user/ABACUSLITE_WRAPPER_GUIDE.md#neb-image-level-mpi) for
|
|
105
|
+
image-parallel setup and the [examples guide](examples/README.md#usage) for
|
|
106
|
+
backend inputs.
|
|
107
|
+
|
|
108
|
+
## Quick Start
|
|
109
|
+
|
|
110
|
+
Obtain examples at the tag matching your installed version (shown for 2.3.2):
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git clone --branch v2.3.2 https://github.com/QuantumMisaka/atst-tools.git
|
|
114
|
+
cd atst-tools
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
First try a local path-preparation command; it needs no ABACUS, DP model or GPU:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cd examples/09_lightweight_cli
|
|
121
|
+
atst neb make inputs/init.xyz inputs/final.xyz 3 -o first_chain.traj --method linear
|
|
122
|
+
atst neb summary inputs/neb_result.extxyz --n-max 1 --tail 5
|
|
123
|
+
cd ../..
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`first_chain.traj` contains five initial-guess frames. To inspect a calculation
|
|
127
|
+
configuration, enter its example directory so relative input paths resolve:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
cd examples/06_relax_H2-Au
|
|
131
|
+
atst config validate config.yaml --print-normalized
|
|
132
|
+
atst run --dry-run config.yaml
|
|
133
|
+
# After configuring the ABACUS runtime and compute resources:
|
|
134
|
+
atst run config.yaml
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This example uses four ABACUS MPI ranks, the GPU `cusolver` solver and a
|
|
138
|
+
one-step optimization limit. Validation and dry-run do not launch ABACUS;
|
|
139
|
+
check the resulting convergence status separately. Follow the
|
|
140
|
+
[examples guide](examples/README.md#usage) to inspect results and choose other
|
|
141
|
+
methods or DP variants. A complete walkthrough is also available in the
|
|
142
|
+
[Chinese guide](docs/user/USER_GUIDE_CN.md#3-10-分钟快速上手).
|
|
143
|
+
|
|
144
|
+
## Documentation
|
|
145
|
+
|
|
146
|
+
| Task | Reference |
|
|
147
|
+
| :--- | :--- |
|
|
148
|
+
| Configure a calculation | [Configuration reference](docs/user/CONFIG_REFERENCE.md) · [All YAML fields](docs/user/YAML_INPUT_VARIABLES.md) |
|
|
149
|
+
| Use CLI commands | [CLI reference](docs/user/CLI_REFERENCE.md) |
|
|
150
|
+
| Configure ABACUS input/output and parallelism | [ABACUS wrapper guide](docs/user/ABACUSLITE_WRAPPER_GUIDE.md) |
|
|
151
|
+
| Embed workflows or use the JSON process runner | [Python API](docs/user/PYTHON_API_REFERENCE.md) |
|
|
152
|
+
| Check support and releases | [Feature matrix](docs/reports/FEATURE_STATUS_MATRIX.md) · [2.3.2 release notes](docs/releases/RELEASE_NOTES_2.3.2.md) |
|
|
153
|
+
| Contribute | [Developer handover](docs/developer/HANDOVER.md) · [Source architecture](src/atst_tools/README.md) |
|
|
154
|
+
|
|
155
|
+
The primary documentation is in English, with a [Chinese user guide](docs/user/USER_GUIDE_CN.md).
|
|
156
|
+
The [documentation index](docs/index.md) organizes user, developer and project
|
|
157
|
+
status material. GitHub is the development source; Gitee is a manually updated
|
|
158
|
+
mirror. PyPI distributes the Python package; obtain examples from the repository.
|
|
159
|
+
|
|
160
|
+
## References
|
|
161
|
+
|
|
162
|
+
If you publish work that uses the transition-state workflows in ATST-Tools,
|
|
163
|
+
please cite the underlying methods alongside ATST-Tools itself:
|
|
164
|
+
|
|
165
|
+
- ATST-Tools and the double-to-single (D2S) transition-state workflow:
|
|
166
|
+
Liu, Z.-Q.; Deng, Z.; Zhao, H.; Wang, H.; Chen, M.; Jiang, H.
|
|
167
|
+
*FT²DP: Large Atomic Model Fine-Tuned Machine Learning Potential for
|
|
168
|
+
Accelerating Atomistic Simulation of Iron-Based Fischer–Tropsch Synthesis.*
|
|
169
|
+
J. Mater. Inform. **5** (2), 27 (2025).
|
|
170
|
+
<https://doi.org/10.20517/jmi.2024.105>
|
|
171
|
+
- Sella saddle-point search:
|
|
172
|
+
Hermes, E. D.; Sargsyan, K.; Najm, H. N.; Zádor, J.
|
|
173
|
+
*Sella, an Open-Source Automation-Friendly Molecular Saddle Point Optimizer.*
|
|
174
|
+
J. Chem. Theory Comput. **18** (11), 6974-6988 (2022).
|
|
175
|
+
<https://doi.org/10.1021/acs.jctc.2c00395>
|
|
176
|
+
- CCQN (cone-shaped constrained quasi-Newton) transition-state optimization:
|
|
177
|
+
Wu, Y.; Wang, H. *Cone-Shaped Constrained Quasi-Newton Method: Efficient
|
|
178
|
+
and Robust Single-Ended Transition State Optimization Algorithm.*
|
|
179
|
+
J. Chem. Theory Comput. **21** (18), 9054-9065 (2025).
|
|
180
|
+
<https://doi.org/10.1021/acs.jctc.5c01015>
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
LGPL-v3 License.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# ATST-Tools
|
|
2
|
+
|
|
3
|
+
[](pyproject.toml)
|
|
4
|
+
[](pyproject.toml)
|
|
5
|
+
[](#license)
|
|
6
|
+
|
|
7
|
+
ATST-Tools is an **ASE transition-state workflow toolkit** for ABACUS and
|
|
8
|
+
DeePMD-kit. Run calculations from YAML with the `atst` command, or use the
|
|
9
|
+
[Python API](docs/user/PYTHON_API_REFERENCE.md) to integrate the same workflows
|
|
10
|
+
into another program.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
atst run config.yaml
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Chinese guide: [中文 · GitHub](https://github.com/QuantumMisaka/atst-tools/blob/main/docs/user/USER_GUIDE_CN.md)
|
|
17
|
+
· [Gitee](https://gitee.com/jamesmisaka/atst-tools/blob/main/docs/user/USER_GUIDE_CN.md).
|
|
18
|
+
Examples: [GitHub](https://github.com/QuantumMisaka/atst-tools/tree/main/examples)
|
|
19
|
+
· [Gitee](https://gitee.com/jamesmisaka/atst-tools/tree/main/examples).
|
|
20
|
+
|
|
21
|
+
## What You Can Run
|
|
22
|
+
|
|
23
|
+
| Workflow | `calculation.type` |
|
|
24
|
+
| :--- | :--- |
|
|
25
|
+
| NEB / DyNEB and adaptive-image NEB | `neb`, `autoneb` |
|
|
26
|
+
| Dimer, Sella and CCQN saddle searches | `dimer`, `sella`, `ccqn` |
|
|
27
|
+
| Double-ended to single-ended TS search | `d2s` |
|
|
28
|
+
| Structure relaxation | `relax` |
|
|
29
|
+
| Vibrations and thermochemistry | `vibration` |
|
|
30
|
+
| Intrinsic reaction coordinates | `irc` |
|
|
31
|
+
| ASE-driven or ABACUS-native molecular dynamics | `md` |
|
|
32
|
+
| Experimental Direct MaxFlux path search | `dmf` |
|
|
33
|
+
| Experimental ABACUS constant-potential single points and serial scans | `constant_potential` |
|
|
34
|
+
|
|
35
|
+
Experimental constant-potential fixed-cell relaxation, NEB and ASE-driven MD
|
|
36
|
+
use the explicit `compensated_gate` boundary. See the
|
|
37
|
+
[configuration reference](docs/user/CONFIG_REFERENCE.md#211-constant-potential-evaluation-development-candidate)
|
|
38
|
+
for setup and supported boundaries.
|
|
39
|
+
|
|
40
|
+
Local commands prepare paths and ABACUS inputs, inspect trajectories, and
|
|
41
|
+
summarize existing results. See the [CLI reference](docs/user/CLI_REFERENCE.md).
|
|
42
|
+
A transition-state candidate can be screened with `atst vibration validate`
|
|
43
|
+
using saved vibration results; workflow completion alone does not establish a
|
|
44
|
+
validated TS. Current support and experimental limits are summarized in the
|
|
45
|
+
[feature matrix](docs/reports/FEATURE_STATUS_MATRIX.md).
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Requires Python 3.10 or later:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install atst-tools
|
|
53
|
+
atst --version
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Use | Installation |
|
|
57
|
+
| :--- | :--- |
|
|
58
|
+
| Serial workflows, including serial NEB/AutoNEB | `pip install atst-tools` |
|
|
59
|
+
| DeePMD-kit calculator | `pip install "atst-tools[dp]"` |
|
|
60
|
+
| Image-parallel NEB/AutoNEB | `pip install "atst-tools[parallel]"` |
|
|
61
|
+
|
|
62
|
+
The default installation includes `abacuslite>=1.2.1,<2` and Sella; it does not
|
|
63
|
+
install `mpi4py`. ABACUS calculations also need an ABACUS executable and the
|
|
64
|
+
pseudopotential/orbital files referenced by the configuration. DP calculations
|
|
65
|
+
need a compatible model. Image-parallel runs require a matching MPI runtime;
|
|
66
|
+
DMF additionally requires `cyipopt` and IPOPT. See the
|
|
67
|
+
[MPI setup guide](docs/user/ABACUSLITE_WRAPPER_GUIDE.md#neb-image-level-mpi) for
|
|
68
|
+
image-parallel setup and the [examples guide](examples/README.md#usage) for
|
|
69
|
+
backend inputs.
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
Obtain examples at the tag matching your installed version (shown for 2.3.2):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone --branch v2.3.2 https://github.com/QuantumMisaka/atst-tools.git
|
|
77
|
+
cd atst-tools
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
First try a local path-preparation command; it needs no ABACUS, DP model or GPU:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cd examples/09_lightweight_cli
|
|
84
|
+
atst neb make inputs/init.xyz inputs/final.xyz 3 -o first_chain.traj --method linear
|
|
85
|
+
atst neb summary inputs/neb_result.extxyz --n-max 1 --tail 5
|
|
86
|
+
cd ../..
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`first_chain.traj` contains five initial-guess frames. To inspect a calculation
|
|
90
|
+
configuration, enter its example directory so relative input paths resolve:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cd examples/06_relax_H2-Au
|
|
94
|
+
atst config validate config.yaml --print-normalized
|
|
95
|
+
atst run --dry-run config.yaml
|
|
96
|
+
# After configuring the ABACUS runtime and compute resources:
|
|
97
|
+
atst run config.yaml
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This example uses four ABACUS MPI ranks, the GPU `cusolver` solver and a
|
|
101
|
+
one-step optimization limit. Validation and dry-run do not launch ABACUS;
|
|
102
|
+
check the resulting convergence status separately. Follow the
|
|
103
|
+
[examples guide](examples/README.md#usage) to inspect results and choose other
|
|
104
|
+
methods or DP variants. A complete walkthrough is also available in the
|
|
105
|
+
[Chinese guide](docs/user/USER_GUIDE_CN.md#3-10-分钟快速上手).
|
|
106
|
+
|
|
107
|
+
## Documentation
|
|
108
|
+
|
|
109
|
+
| Task | Reference |
|
|
110
|
+
| :--- | :--- |
|
|
111
|
+
| Configure a calculation | [Configuration reference](docs/user/CONFIG_REFERENCE.md) · [All YAML fields](docs/user/YAML_INPUT_VARIABLES.md) |
|
|
112
|
+
| Use CLI commands | [CLI reference](docs/user/CLI_REFERENCE.md) |
|
|
113
|
+
| Configure ABACUS input/output and parallelism | [ABACUS wrapper guide](docs/user/ABACUSLITE_WRAPPER_GUIDE.md) |
|
|
114
|
+
| Embed workflows or use the JSON process runner | [Python API](docs/user/PYTHON_API_REFERENCE.md) |
|
|
115
|
+
| Check support and releases | [Feature matrix](docs/reports/FEATURE_STATUS_MATRIX.md) · [2.3.2 release notes](docs/releases/RELEASE_NOTES_2.3.2.md) |
|
|
116
|
+
| Contribute | [Developer handover](docs/developer/HANDOVER.md) · [Source architecture](src/atst_tools/README.md) |
|
|
117
|
+
|
|
118
|
+
The primary documentation is in English, with a [Chinese user guide](docs/user/USER_GUIDE_CN.md).
|
|
119
|
+
The [documentation index](docs/index.md) organizes user, developer and project
|
|
120
|
+
status material. GitHub is the development source; Gitee is a manually updated
|
|
121
|
+
mirror. PyPI distributes the Python package; obtain examples from the repository.
|
|
122
|
+
|
|
123
|
+
## References
|
|
124
|
+
|
|
125
|
+
If you publish work that uses the transition-state workflows in ATST-Tools,
|
|
126
|
+
please cite the underlying methods alongside ATST-Tools itself:
|
|
127
|
+
|
|
128
|
+
- ATST-Tools and the double-to-single (D2S) transition-state workflow:
|
|
129
|
+
Liu, Z.-Q.; Deng, Z.; Zhao, H.; Wang, H.; Chen, M.; Jiang, H.
|
|
130
|
+
*FT²DP: Large Atomic Model Fine-Tuned Machine Learning Potential for
|
|
131
|
+
Accelerating Atomistic Simulation of Iron-Based Fischer–Tropsch Synthesis.*
|
|
132
|
+
J. Mater. Inform. **5** (2), 27 (2025).
|
|
133
|
+
<https://doi.org/10.20517/jmi.2024.105>
|
|
134
|
+
- Sella saddle-point search:
|
|
135
|
+
Hermes, E. D.; Sargsyan, K.; Najm, H. N.; Zádor, J.
|
|
136
|
+
*Sella, an Open-Source Automation-Friendly Molecular Saddle Point Optimizer.*
|
|
137
|
+
J. Chem. Theory Comput. **18** (11), 6974-6988 (2022).
|
|
138
|
+
<https://doi.org/10.1021/acs.jctc.2c00395>
|
|
139
|
+
- CCQN (cone-shaped constrained quasi-Newton) transition-state optimization:
|
|
140
|
+
Wu, Y.; Wang, H. *Cone-Shaped Constrained Quasi-Newton Method: Efficient
|
|
141
|
+
and Robust Single-Ended Transition State Optimization Algorithm.*
|
|
142
|
+
J. Chem. Theory Comput. **21** (18), 9054-9065 (2025).
|
|
143
|
+
<https://doi.org/10.1021/acs.jctc.5c01015>
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
LGPL-v3 License.
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "atst-tools"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.2"
|
|
8
8
|
description = "Advanced ASE Transition State Tools for ABACUS and Deep-Potential"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "LGPL-3.0-or-later"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
dependencies = [
|
|
13
|
-
"abacuslite>=1.2.
|
|
13
|
+
"abacuslite>=1.2.1,<2",
|
|
14
14
|
"ase>=3.28.0",
|
|
15
15
|
"numpy>=1.26,<3",
|
|
16
16
|
"scipy>=1.13,<2",
|
|
@@ -17,7 +17,7 @@ BackendSource = Literal["external"]
|
|
|
17
17
|
def _load_abacuslite_backend():
|
|
18
18
|
if find_spec("abacuslite") is None:
|
|
19
19
|
raise ModuleNotFoundError(
|
|
20
|
-
"abacuslite is required by ATST-Tools; install abacuslite>=1.2.
|
|
20
|
+
"abacuslite is required by ATST-Tools; install abacuslite>=1.2.1,<2",
|
|
21
21
|
name="abacuslite",
|
|
22
22
|
)
|
|
23
23
|
from abacuslite import Abacus, AbacusProfile
|
|
@@ -1074,6 +1074,14 @@ class ConstantPotentialCalculator(Calculator):
|
|
|
1074
1074
|
identity["fixed_hamiltonian"] = copy.deepcopy(self._fixed_hamiltonian_identity)
|
|
1075
1075
|
return identity
|
|
1076
1076
|
|
|
1077
|
+
def stage_initial_electrons(self, nelec: float) -> None:
|
|
1078
|
+
"""Stage a validated next-solve guess without clearing current results.
|
|
1079
|
+
|
|
1080
|
+
This is for sequential CP MD evaluations. The electron count is only
|
|
1081
|
+
an initial guess; the next geometry still runs the complete CP loop.
|
|
1082
|
+
"""
|
|
1083
|
+
self.initial_electrons = self._validate_candidate(_finite(nelec, "initial_electrons"))
|
|
1084
|
+
|
|
1077
1085
|
def set(self, **kwargs: Any) -> dict[str, Any]:
|
|
1078
1086
|
"""Update CP parameters and invalidate cached ASE results."""
|
|
1079
1087
|
changed = super().set(**kwargs)
|
|
@@ -1254,8 +1262,12 @@ class ConstantPotentialCalculator(Calculator):
|
|
|
1254
1262
|
# consumes the published CP result. Suppress them on this private
|
|
1255
1263
|
# probe so Hookean/constraint energy and force contributions are
|
|
1256
1264
|
# not applied a second time inside the electronic loop.
|
|
1257
|
-
|
|
1258
|
-
|
|
1265
|
+
kohn_sham_energy = float(
|
|
1266
|
+
probe.get_potential_energy(force_consistent=False, apply_constraint=False)
|
|
1267
|
+
)
|
|
1268
|
+
force_consistent_energy = float(
|
|
1269
|
+
probe.get_potential_energy(force_consistent=True, apply_constraint=False)
|
|
1270
|
+
)
|
|
1259
1271
|
forces = np.asarray(probe.get_forces(apply_constraint=False), dtype=float)
|
|
1260
1272
|
except ConstantPotentialError:
|
|
1261
1273
|
raise
|
|
@@ -1274,12 +1286,17 @@ class ConstantPotentialCalculator(Calculator):
|
|
|
1274
1286
|
raise ConstantPotentialEvaluationError(
|
|
1275
1287
|
f"fixed-electron evaluation at nelec={nelec} returned invalid forces"
|
|
1276
1288
|
)
|
|
1289
|
+
# Preserve the backend's ASE energy keys for the compensated boundary.
|
|
1290
|
+
# The reference-only FCP convention uses the explicit no-entropy
|
|
1291
|
+
# energy, without changing the force-consistent free energy.
|
|
1277
1292
|
raw_energy = _first_finite(raw_results, ("energy",))
|
|
1278
1293
|
raw_free_energy = _first_finite(raw_results, ("free_energy",))
|
|
1279
1294
|
if raw_energy is None:
|
|
1280
|
-
raw_energy = _finite(
|
|
1295
|
+
raw_energy = _finite(kohn_sham_energy, "raw energy")
|
|
1281
1296
|
if raw_free_energy is None:
|
|
1282
|
-
raw_free_energy = _finite(
|
|
1297
|
+
raw_free_energy = _finite(force_consistent_energy, "raw free energy")
|
|
1298
|
+
if self.energy_boundary == "reference_fcp" and "energy_without_entropy" in raw_results:
|
|
1299
|
+
raw_energy = _finite(raw_results["energy_without_entropy"], "energy without entropy")
|
|
1283
1300
|
efermi = _backend_efermi(inner, raw_results)
|
|
1284
1301
|
if efermi is None:
|
|
1285
1302
|
raise ConstantPotentialEvaluationError(
|
|
@@ -1534,6 +1551,7 @@ class ConstantPotentialCalculator(Calculator):
|
|
|
1534
1551
|
"scf_converged": True,
|
|
1535
1552
|
"cp_converged": True,
|
|
1536
1553
|
"cp_iterations": int(final["evaluation_index"]),
|
|
1554
|
+
"capacitance_e_per_V": float(capacitance),
|
|
1537
1555
|
"cp_history": self._jsonable(self.evaluation_history),
|
|
1538
1556
|
"cp_identity": self.constant_potential_identity,
|
|
1539
1557
|
}
|
|
@@ -22,6 +22,10 @@ from atst_tools.calculators.constant_potential import (
|
|
|
22
22
|
from atst_tools.calculators.dp import DeepPotentialFactory
|
|
23
23
|
from atst_tools.runtime import counters as runtime_counters
|
|
24
24
|
from atst_tools.runtime import launch as runtime_launch
|
|
25
|
+
from atst_tools.utils.config_schema import (
|
|
26
|
+
ConstantPotentialConfig,
|
|
27
|
+
validate_constant_potential_md_scope,
|
|
28
|
+
)
|
|
25
29
|
from atst_tools.utils.mpi import mpi_launcher_detected
|
|
26
30
|
|
|
27
31
|
|
|
@@ -228,14 +232,18 @@ def _constant_potential_calculator(
|
|
|
228
232
|
config: Dict[str, Any], cp_config: Dict[str, Any], kwargs: Dict[str, Any]
|
|
229
233
|
) -> ConstantPotentialCalculator:
|
|
230
234
|
"""Build one CP decorator around fresh, unwrapped ABACUS calculators."""
|
|
231
|
-
supported_workflows = {"constant_potential", "relax", "neb"}
|
|
232
|
-
|
|
235
|
+
supported_workflows = {"constant_potential", "relax", "neb", "md"}
|
|
236
|
+
calculation = config.get("calculation", {})
|
|
237
|
+
workflow = calculation.get("type") if isinstance(calculation, dict) else None
|
|
233
238
|
if workflow is not None and workflow not in supported_workflows:
|
|
234
239
|
raise ValueError(
|
|
235
240
|
"calculator.constant_potential is supported only for calculation.type in "
|
|
236
241
|
f"{sorted(supported_workflows)}; got {workflow!r}"
|
|
237
242
|
)
|
|
238
243
|
options = dict(cp_config)
|
|
244
|
+
if workflow == "md":
|
|
245
|
+
validate_constant_potential_md_scope(calculation, options)
|
|
246
|
+
options = ConstantPotentialConfig.model_validate(options).model_dump(mode="python")
|
|
239
247
|
energy_boundary = str(options.get("energy_boundary", "reference_fcp"))
|
|
240
248
|
if energy_boundary not in {"reference_fcp", "compensated_gate"}:
|
|
241
249
|
raise ValueError("calculator.constant_potential.energy_boundary is unsupported")
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import math
|
|
6
6
|
from types import UnionType
|
|
7
|
-
from typing import Annotated, Any, Dict, Literal, Union, get_args, get_origin
|
|
7
|
+
from typing import Annotated, Any, Dict, Literal, Mapping, Union, get_args, get_origin
|
|
8
8
|
|
|
9
9
|
from pydantic import (
|
|
10
10
|
AliasChoices,
|
|
@@ -144,7 +144,7 @@ class ConstantPotentialConfig(StrictConfig):
|
|
|
144
144
|
target_mu_ev: float | None = Field(
|
|
145
145
|
default=None,
|
|
146
146
|
allow_inf_nan=False,
|
|
147
|
-
description="
|
|
147
|
+
description="Scalar target chemical potential in eV for compensated_gate; required for CP MD.",
|
|
148
148
|
)
|
|
149
149
|
target_mu_values_ev: list[float] | None = Field(
|
|
150
150
|
default=None,
|
|
@@ -868,8 +868,12 @@ class MDCalculation(StrictConfig):
|
|
|
868
868
|
)
|
|
869
869
|
init_structure: str = Field(description="Initial structure file.")
|
|
870
870
|
steps: int = Field(default=100, gt=0, description="Number of MD steps.")
|
|
871
|
-
ensemble: Literal["nve", "nvt", "npt"] = Field(
|
|
872
|
-
|
|
871
|
+
ensemble: Literal["nve", "nvt", "npt"] = Field(
|
|
872
|
+
default="nvt", description="ASE MD ensemble; CP MD supports fixed-cell nve and nvt only."
|
|
873
|
+
)
|
|
874
|
+
algorithm: str = Field(
|
|
875
|
+
default="bussi", description="ASE MD algorithm; CP MD supports velocityverlet, bussi, langevin and nvtberendsen."
|
|
876
|
+
)
|
|
873
877
|
timestep_fs: float = Field(default=1.0, gt=0, description="ASE MD timestep in fs.")
|
|
874
878
|
temperature_K: float = Field(default=300.0, gt=0, description="Target or initial temperature in K.")
|
|
875
879
|
seed: int | None = Field(default=None, description="Random seed for initial velocities.")
|
|
@@ -911,6 +915,46 @@ class MDCalculation(StrictConfig):
|
|
|
911
915
|
return self
|
|
912
916
|
|
|
913
917
|
|
|
918
|
+
def validate_constant_potential_md_scope(
|
|
919
|
+
calculation: Mapping[str, Any], constant_potential: Mapping[str, Any]
|
|
920
|
+
) -> None:
|
|
921
|
+
"""Reject MD/CP combinations outside the fixed-cell ASE contract.
|
|
922
|
+
|
|
923
|
+
Args:
|
|
924
|
+
calculation: Raw or normalized MD calculation settings.
|
|
925
|
+
constant_potential: Raw or normalized CP decorator settings.
|
|
926
|
+
|
|
927
|
+
Raises:
|
|
928
|
+
ValueError: If the MD driver, integrator, or electronic boundary is unsupported.
|
|
929
|
+
"""
|
|
930
|
+
if calculation.get("driver", "ase") != "ase":
|
|
931
|
+
raise ValueError("constant-potential MD requires calculation.driver=ase")
|
|
932
|
+
ensemble = calculation.get("ensemble", "nvt")
|
|
933
|
+
if ensemble not in {"nve", "nvt"}:
|
|
934
|
+
raise ValueError("constant-potential MD requires a fixed-cell nve or nvt ensemble")
|
|
935
|
+
algorithm = str(calculation.get("algorithm", "bussi")).lower()
|
|
936
|
+
allowed = {"nve": {"velocityverlet"}, "nvt": {"bussi", "langevin", "nvtberendsen"}}
|
|
937
|
+
if algorithm not in allowed[ensemble]:
|
|
938
|
+
raise ValueError(
|
|
939
|
+
f"calculation.algorithm={algorithm!r} is not supported for constant-potential MD ensemble={ensemble!r}"
|
|
940
|
+
)
|
|
941
|
+
if constant_potential.get("energy_boundary") != "compensated_gate":
|
|
942
|
+
raise ValueError("constant-potential MD requires energy_boundary=compensated_gate")
|
|
943
|
+
if str(constant_potential.get("reference_electrode", "SHE")).strip().lower() != "custom":
|
|
944
|
+
raise ValueError("constant-potential MD requires reference_electrode=custom")
|
|
945
|
+
target = constant_potential.get("target_mu_ev")
|
|
946
|
+
if constant_potential.get("target_mu_values_ev") is not None or constant_potential.get("potentials_v") is not None:
|
|
947
|
+
raise ValueError("constant-potential MD requires one scalar target_mu_ev, not a target list")
|
|
948
|
+
if target is None or isinstance(target, (bool, list, tuple, dict)):
|
|
949
|
+
raise ValueError("constant-potential MD requires one scalar target_mu_ev")
|
|
950
|
+
try:
|
|
951
|
+
finite_target = math.isfinite(float(target))
|
|
952
|
+
except (TypeError, ValueError):
|
|
953
|
+
finite_target = False
|
|
954
|
+
if not finite_target:
|
|
955
|
+
raise ValueError("constant-potential MD requires one finite scalar target_mu_ev")
|
|
956
|
+
|
|
957
|
+
|
|
914
958
|
CalculationConfig = Annotated[
|
|
915
959
|
Union[
|
|
916
960
|
ConstantPotentialCalculation,
|
|
@@ -1075,7 +1119,7 @@ class CalculatorConfig(StrictConfig):
|
|
|
1075
1119
|
dp: DPConfig | None = Field(default=None, description="DeepMD-kit calculator settings.")
|
|
1076
1120
|
constant_potential: ConstantPotentialConfig | None = Field(
|
|
1077
1121
|
default=None,
|
|
1078
|
-
description="Optional strict constant-potential decorator settings.",
|
|
1122
|
+
description="Optional strict constant-potential decorator settings; MD requires ASE fixed-cell compensated_gate with one scalar target.",
|
|
1079
1123
|
)
|
|
1080
1124
|
|
|
1081
1125
|
@model_validator(mode="after")
|
|
@@ -1233,10 +1277,14 @@ class ATSTConfig(StrictConfig):
|
|
|
1233
1277
|
if merged_parameters.get("two_fermi"):
|
|
1234
1278
|
raise ValueError("calculator.constant_potential does not support two_fermi")
|
|
1235
1279
|
workflow = self.calculation.type
|
|
1236
|
-
if workflow not in {"constant_potential", "relax", "neb"}:
|
|
1280
|
+
if workflow not in {"constant_potential", "relax", "neb", "md"}:
|
|
1237
1281
|
raise ValueError(
|
|
1238
1282
|
"calculator.constant_potential is supported only for calculation.type="
|
|
1239
|
-
"constant_potential, relax, or
|
|
1283
|
+
"constant_potential, relax, neb, or md"
|
|
1284
|
+
)
|
|
1285
|
+
if workflow == "md":
|
|
1286
|
+
validate_constant_potential_md_scope(
|
|
1287
|
+
self.calculation.model_dump(mode="python"), cp.model_dump(mode="python")
|
|
1240
1288
|
)
|
|
1241
1289
|
if workflow in {"relax", "neb"} and (
|
|
1242
1290
|
cp.potentials_v is not None or cp.target_mu_values_ev is not None
|
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import math
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
from ase.io import read, write
|
|
10
11
|
|
|
12
|
+
from atst_tools.calculators.constant_potential import (
|
|
13
|
+
CP_FACTS_INFO_KEY,
|
|
14
|
+
CP_IDENTITY_INFO_KEY,
|
|
15
|
+
read_constant_potential_facts,
|
|
16
|
+
)
|
|
11
17
|
from atst_tools.utils.summary import max_force, energy, write_summary_json
|
|
12
18
|
|
|
13
19
|
|
|
@@ -36,6 +42,70 @@ def _write_md_summary(summary: dict[str, Any], output: str | Path) -> None:
|
|
|
36
42
|
write_summary_json(summary, output)
|
|
37
43
|
|
|
38
44
|
|
|
45
|
+
def _saved_step_time(atoms: Any, frame_index: int) -> dict[str, Any]:
|
|
46
|
+
"""Return saved MD progress, falling back to frame index for old trajectories."""
|
|
47
|
+
step_key = "atst_md_step"
|
|
48
|
+
time_key = "atst_md_time_fs"
|
|
49
|
+
has_step = step_key in atoms.info
|
|
50
|
+
has_time = time_key in atoms.info
|
|
51
|
+
if not has_step and not has_time:
|
|
52
|
+
return {"step": frame_index}
|
|
53
|
+
if not has_step:
|
|
54
|
+
raise ValueError(f"saved MD frame is missing {step_key}")
|
|
55
|
+
if not has_time:
|
|
56
|
+
raise ValueError(f"saved MD frame is missing {time_key}")
|
|
57
|
+
step = atoms.info[step_key]
|
|
58
|
+
if isinstance(step, (bool, np.bool_)) or not isinstance(step, (int, np.integer)) or step < 0:
|
|
59
|
+
raise ValueError(f"saved MD frame has invalid {step_key}")
|
|
60
|
+
time_fs = atoms.info[time_key]
|
|
61
|
+
if (
|
|
62
|
+
isinstance(time_fs, (bool, np.bool_))
|
|
63
|
+
or not isinstance(time_fs, (int, float, np.integer, np.floating))
|
|
64
|
+
or not math.isfinite(float(time_fs))
|
|
65
|
+
or time_fs < 0
|
|
66
|
+
):
|
|
67
|
+
raise ValueError(f"saved MD frame has invalid {time_key}")
|
|
68
|
+
return {"step": int(step), "time_fs": float(time_fs)}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _saved_constant_potential(atoms: Any) -> dict[str, Any] | None:
|
|
72
|
+
"""Summarize only a complete CP facts envelope for the saved geometry."""
|
|
73
|
+
if CP_FACTS_INFO_KEY not in atoms.info and CP_IDENTITY_INFO_KEY not in atoms.info:
|
|
74
|
+
return None
|
|
75
|
+
payload = read_constant_potential_facts(atoms)
|
|
76
|
+
if payload is None:
|
|
77
|
+
raise ValueError("saved MD frame has malformed or stale constant-potential facts")
|
|
78
|
+
facts = payload["facts"]
|
|
79
|
+
return {
|
|
80
|
+
"energy_boundary": facts["energy_boundary"],
|
|
81
|
+
"energy_definition": payload["identity"]["energy_definition"],
|
|
82
|
+
"nelec": float(facts["nelec"]),
|
|
83
|
+
"mu_target_ev": float(facts["mu_target"]),
|
|
84
|
+
"mu_calc_ev": float(facts["mu_calc"]),
|
|
85
|
+
"residual_mu_ev": float(facts["residual_mu"]),
|
|
86
|
+
"cp_iterations": int(facts["cp_iterations"]),
|
|
87
|
+
"raw_energy_eV": float(facts["raw_energy"]),
|
|
88
|
+
"raw_free_energy_eV": float(facts["raw_free_energy"]),
|
|
89
|
+
"grand_potential_eV": float(facts["energy"]),
|
|
90
|
+
"grand_potential_free_energy_eV": float(facts["free_energy"]),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _frame_summary(atoms: Any, frame_index: int) -> dict[str, Any]:
|
|
95
|
+
"""Return one trajectory frame summary from saved nuclear and CP state."""
|
|
96
|
+
progress = _saved_step_time(atoms, frame_index)
|
|
97
|
+
cp = _saved_constant_potential(atoms)
|
|
98
|
+
summary = {
|
|
99
|
+
**progress,
|
|
100
|
+
"energy_eV": energy(atoms),
|
|
101
|
+
"temperature_K": _temperature(atoms),
|
|
102
|
+
"max_force_eV_per_A": max_force(atoms),
|
|
103
|
+
}
|
|
104
|
+
if cp is not None:
|
|
105
|
+
summary["constant_potential"] = cp
|
|
106
|
+
return summary
|
|
107
|
+
|
|
108
|
+
|
|
39
109
|
def summarize_md_trajectory(
|
|
40
110
|
traj_file: str | Path,
|
|
41
111
|
*,
|
|
@@ -44,15 +114,7 @@ def summarize_md_trajectory(
|
|
|
44
114
|
) -> dict[str, Any]:
|
|
45
115
|
"""Read and summarize an MD trajectory."""
|
|
46
116
|
frames = read(str(traj_file), index=":")
|
|
47
|
-
frame_summaries = [
|
|
48
|
-
{
|
|
49
|
-
"step": index,
|
|
50
|
-
"energy_eV": energy(atoms),
|
|
51
|
-
"temperature_K": _temperature(atoms),
|
|
52
|
-
"max_force_eV_per_A": max_force(atoms),
|
|
53
|
-
}
|
|
54
|
-
for index, atoms in enumerate(frames)
|
|
55
|
-
]
|
|
117
|
+
frame_summaries = [_frame_summary(atoms, index) for index, atoms in enumerate(frames)]
|
|
56
118
|
selected = frame_summaries[-tail:] if tail and tail > 0 else frame_summaries
|
|
57
119
|
latest = dict(frame_summaries[-1]) if frame_summaries else {}
|
|
58
120
|
return {
|