GraphAtoms 2.3.0__tar.gz → 2.3.1__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.
- {graphatoms-2.3.0 → graphatoms-2.3.1}/PKG-INFO +1 -1
- {graphatoms-2.3.0 → graphatoms-2.3.1}/pyproject.toml +2 -2
- {graphatoms-2.3.0 → graphatoms-2.3.1}/pyproject.toml.orig +2 -2
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/network/__init__.py +2 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/network/_clsnetwork.py +220 -81
- graphatoms-2.3.1/src/graphatoms/enterpoint/network/_commands.py +139 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/network/_metadata.py +47 -1
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/network/_recorder.py +17 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/network/_scheduler.py +4 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/parallel/_ray.py +23 -4
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/general/_expl.py +11 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/ray/_expl.py +8 -9
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/ray/_helper.py +4 -4
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/reaction/_event.py +56 -18
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/bonds.py +9 -5
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/database/abc.py +1 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/sysCluster.py +44 -6
- graphatoms-2.3.0/src/graphatoms/enterpoint/view.py +0 -118
- {graphatoms-2.3.0 → graphatoms-2.3.1}/README.md +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/LICENSE +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/arrayapi/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/arrayapi/compat.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/arrayapi/typing.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/dataclasses/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/dataclasses/_numpydantic.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/dataclasses/_pydantic2pyarrow.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/dataclasses/_pydanticMixin.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/dataclasses/_pydanticModel.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/dataclasses/graph.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/atoms/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/atoms/aseread.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/atoms/octahedron.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/bonds/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/bonds/raw.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/calculator/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/calculator/emt.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/calculator/nequip.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/example.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/config/run.yaml +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/parallel/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/parallel/_base.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/parallel/_dask.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/parallel/_executorlib.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/common/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/common/_base.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/common/_errors.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/common/_helper.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/general/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/general/_expl.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/general/otfkmc.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/general/rxngen.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/_helperv2.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/general/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/general/otfkmc.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/general/rxngen.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/ray/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/ray/otfkmc.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/enterpoint/runner/v2/ray/rxngen.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/_bond_list.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/_distance_pairs.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/_inner_outer.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/_neighbor_list.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/mic.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/rotation.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/geometry/sample.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/reaction/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/reaction/reaction.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/reaction/xxsorption.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/atoms.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/database/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/database/dict.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/database/dir.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/database/hdf5.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/database/sqliteASE.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/graph.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/sysGas.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/system/system.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/__init__.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/adsorption.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/aseqhull.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/asetools.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/bytestool.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/logger.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/parser.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/rdutils.py +0 -0
- {graphatoms-2.3.0 → graphatoms-2.3.1}/src/graphatoms/utils/subgraph.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "graphatoms"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.1"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
description = "The Chemical Core Class for Graph Theory Analysis."
|
|
10
10
|
license-files = ["src/LICENSE"]
|
|
@@ -56,7 +56,7 @@ Issues = "https://github.com/LiuGaoyong/GraphAtoms/issues/"
|
|
|
56
56
|
|
|
57
57
|
[project.scripts]
|
|
58
58
|
graphatoms-config = "graphatoms.enterpoint.config:print_config"
|
|
59
|
-
graphatoms-network = "graphatoms.enterpoint.
|
|
59
|
+
graphatoms-network = "graphatoms.enterpoint.network:main"
|
|
60
60
|
graphatoms-run = "graphatoms.enterpoint.runner:run"
|
|
61
61
|
|
|
62
62
|
[[project.authors]]
|
|
@@ -8,11 +8,11 @@ Repository = "https://github.com/LiuGaoyong/GraphAtoms"
|
|
|
8
8
|
Issues = "https://github.com/LiuGaoyong/GraphAtoms/issues/"
|
|
9
9
|
[project.scripts]
|
|
10
10
|
graphatoms-config = "graphatoms.enterpoint.config:print_config"
|
|
11
|
-
graphatoms-network = "graphatoms.enterpoint.
|
|
11
|
+
graphatoms-network = "graphatoms.enterpoint.network:main"
|
|
12
12
|
graphatoms-run = "graphatoms.enterpoint.runner:run"
|
|
13
13
|
[project]
|
|
14
14
|
name = "graphatoms"
|
|
15
|
-
version = "2.3.
|
|
15
|
+
version = "2.3.1"
|
|
16
16
|
readme = "README.md"
|
|
17
17
|
description = "The Chemical Core Class for Graph Theory Analysis."
|
|
18
18
|
authors = [{ name = "LiuGaoyong", email = "liugaoyong_88@163.com" }]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from ._clsnetwork import ReactionNetwork
|
|
2
|
+
from ._commands import main
|
|
2
3
|
from ._metadata import MetaData
|
|
3
4
|
from ._recorder import Recorder
|
|
4
5
|
from ._scheduler import Scheduler
|
|
@@ -8,4 +9,5 @@ __all__ = [
|
|
|
8
9
|
"Recorder",
|
|
9
10
|
"Scheduler",
|
|
10
11
|
"ReactionNetwork",
|
|
12
|
+
"main",
|
|
11
13
|
]
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import pickle
|
|
2
2
|
import time
|
|
3
|
+
from collections.abc import Iterator, Mapping
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import Literal, Self
|
|
5
|
+
from typing import Literal, Self, override
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
from ase import Atoms
|
|
8
9
|
|
|
9
10
|
from graphatoms.enterpoint.config import Config, EventConfig
|
|
11
|
+
from graphatoms.enterpoint.network._metadata import MetaData
|
|
12
|
+
from graphatoms.enterpoint.network._metadata import (
|
|
13
|
+
_MetaDataBasic as MetaDataBasic,
|
|
14
|
+
)
|
|
15
|
+
from graphatoms.enterpoint.network._recorder import (
|
|
16
|
+
OldNewRecorder4Confidence,
|
|
17
|
+
Recorder,
|
|
18
|
+
RecorderInfo,
|
|
19
|
+
)
|
|
20
|
+
from graphatoms.enterpoint.network._scheduler import Scheduler
|
|
10
21
|
from graphatoms.reaction import (
|
|
11
22
|
Adsorption,
|
|
12
23
|
Desorption,
|
|
@@ -17,11 +28,6 @@ from graphatoms.reaction import (
|
|
|
17
28
|
from graphatoms.system import Cluster, Gas, SysGraph, System
|
|
18
29
|
from graphatoms.system.database import DatabaseABC, get_db
|
|
19
30
|
|
|
20
|
-
from ._metadata import MetaData
|
|
21
|
-
from ._metadata import _MetaDataBasic as MetaDataBasic
|
|
22
|
-
from ._recorder import OldNewRecorder4Confidence, Recorder, RecorderInfo
|
|
23
|
-
from ._scheduler import Scheduler
|
|
24
|
-
|
|
25
31
|
__all__ = [
|
|
26
32
|
"MetaData",
|
|
27
33
|
"Recorder",
|
|
@@ -31,7 +37,7 @@ __all__ = [
|
|
|
31
37
|
NETWORK_PATH_PREFIX = "event"
|
|
32
38
|
|
|
33
39
|
|
|
34
|
-
class ReactionNetwork:
|
|
40
|
+
class ReactionNetwork(Mapping[str, tuple[EventInfo, EventBase]]):
|
|
35
41
|
DB_USE_POSITION_UUID_DICT: dict[str, bool] = {
|
|
36
42
|
"ts": True,
|
|
37
43
|
"minima": True,
|
|
@@ -57,8 +63,8 @@ class ReactionNetwork:
|
|
|
57
63
|
assert format in (
|
|
58
64
|
"sqlite",
|
|
59
65
|
"directory",
|
|
60
|
-
"dir",
|
|
61
66
|
"folder",
|
|
67
|
+
"dir",
|
|
62
68
|
"h5",
|
|
63
69
|
"hdf5",
|
|
64
70
|
), f"The format of `{format}` is not supported for restart."
|
|
@@ -118,6 +124,170 @@ class ReactionNetwork:
|
|
|
118
124
|
assert isinstance(self.__db_cluster, DatabaseABC)
|
|
119
125
|
assert isinstance(self.__db_system, DatabaseABC)
|
|
120
126
|
|
|
127
|
+
@classmethod
|
|
128
|
+
def from_config(
|
|
129
|
+
cls,
|
|
130
|
+
config: Config,
|
|
131
|
+
*args,
|
|
132
|
+
restart: bool | None = None,
|
|
133
|
+
remote_mode: bool = False,
|
|
134
|
+
**kwargs,
|
|
135
|
+
) -> Self: # type: ignore
|
|
136
|
+
"""Create a network from a config file."""
|
|
137
|
+
if remote_mode is True:
|
|
138
|
+
# When remote_mode is True, some files may not exist or in write.
|
|
139
|
+
# We need to try to create the network again.
|
|
140
|
+
err = None
|
|
141
|
+
for _ in range(10):
|
|
142
|
+
try:
|
|
143
|
+
return cls(
|
|
144
|
+
path=Path(config.outputs) / NETWORK_PATH_PREFIX,
|
|
145
|
+
restart=restart
|
|
146
|
+
if restart is not None
|
|
147
|
+
else config.restart,
|
|
148
|
+
format=config.event.db_format,
|
|
149
|
+
remote_mode=remote_mode,
|
|
150
|
+
config=config.event,
|
|
151
|
+
**kwargs,
|
|
152
|
+
)
|
|
153
|
+
break
|
|
154
|
+
except Exception as e:
|
|
155
|
+
time.sleep(0.5)
|
|
156
|
+
err = e
|
|
157
|
+
if err is not None:
|
|
158
|
+
raise RuntimeError(
|
|
159
|
+
f"Failed to create the network in remote mode. {err}"
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
return cls(
|
|
163
|
+
path=Path(config.outputs) / NETWORK_PATH_PREFIX,
|
|
164
|
+
restart=restart if restart is not None else config.restart,
|
|
165
|
+
format=config.event.db_format,
|
|
166
|
+
remote_mode=remote_mode,
|
|
167
|
+
config=config.event,
|
|
168
|
+
**kwargs,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def merge(
|
|
173
|
+
cls,
|
|
174
|
+
*networks: Self,
|
|
175
|
+
new_path: Path | str,
|
|
176
|
+
new_format: str | Literal["dir", "h5", "sqlite"] = "dir",
|
|
177
|
+
**kwargs,
|
|
178
|
+
) -> Self:
|
|
179
|
+
"""Merge the networks.
|
|
180
|
+
|
|
181
|
+
Note: the recorder will be set empty.
|
|
182
|
+
"""
|
|
183
|
+
if len(networks) <= 1:
|
|
184
|
+
raise ValueError("At least two network is required.")
|
|
185
|
+
result = cls(
|
|
186
|
+
path=Path(new_path),
|
|
187
|
+
restart=False,
|
|
188
|
+
config=networks[0].metadata.basic,
|
|
189
|
+
format=new_format,
|
|
190
|
+
)
|
|
191
|
+
result.metadata.basic.db_format = new_format
|
|
192
|
+
for network in networks[1:]:
|
|
193
|
+
try:
|
|
194
|
+
network.metadata.basic.check_consistency(
|
|
195
|
+
result.metadata.basic,
|
|
196
|
+
exclude_keys={"db_format", "gas_sticking"},
|
|
197
|
+
)
|
|
198
|
+
for k, v in network.metadata.basic.gas_sticking.items():
|
|
199
|
+
if k not in result.metadata.basic.gas_sticking:
|
|
200
|
+
result.metadata.basic.gas_sticking[k] = v
|
|
201
|
+
else:
|
|
202
|
+
v0 = result.metadata.basic.gas_sticking[k]
|
|
203
|
+
assert v0 == v, (
|
|
204
|
+
f"Failed to merge the networks because the "
|
|
205
|
+
f"gas sticking of {k} is not the same as "
|
|
206
|
+
f"input. self={v0}, input={v}"
|
|
207
|
+
)
|
|
208
|
+
result.metadata.basic.gas_sticking[k] = v
|
|
209
|
+
except Exception as e:
|
|
210
|
+
raise ValueError(
|
|
211
|
+
f"Failed to merge the networks because "
|
|
212
|
+
f"of check consistency failed: {e}"
|
|
213
|
+
)
|
|
214
|
+
for k, use_pos_uuid in cls.DB_USE_POSITION_UUID_DICT.items():
|
|
215
|
+
db_res: DatabaseABC = getattr(result, f"db_{k}")
|
|
216
|
+
db: DatabaseABC = getattr(network, f"db_{k}")
|
|
217
|
+
for _, v in db.items():
|
|
218
|
+
graph = SysGraph.from_ase(v)
|
|
219
|
+
graph_key = graph.get_key_for_metadata(use_pos_uuid)
|
|
220
|
+
if graph_key not in db_res:
|
|
221
|
+
db_res._save(graph_key, graph)
|
|
222
|
+
for i, rxn in enumerate(network.metadata.table.key_rxn):
|
|
223
|
+
if rxn not in result.metadata.table.key_rxn:
|
|
224
|
+
key_r = network.metadata.table.key_r[i]
|
|
225
|
+
key_p = network.metadata.table.key_p[i]
|
|
226
|
+
key_g = network.metadata.table.key_g[i]
|
|
227
|
+
key_ts = network.metadata.table.key_t[i]
|
|
228
|
+
if key_r not in result.db_minima:
|
|
229
|
+
print(
|
|
230
|
+
f"Find invalid key: {rxn}\n"
|
|
231
|
+
f" Missing key of reactant: {key_r}\n",
|
|
232
|
+
flush=True,
|
|
233
|
+
)
|
|
234
|
+
continue
|
|
235
|
+
elif key_g is not None and key_g not in result.db_gas:
|
|
236
|
+
print(
|
|
237
|
+
f"Find invalid key: {rxn}\n"
|
|
238
|
+
f" Missing key of gas: {key_g}\n",
|
|
239
|
+
flush=True,
|
|
240
|
+
)
|
|
241
|
+
continue
|
|
242
|
+
elif key_ts is not None and key_ts not in result.db_ts:
|
|
243
|
+
print(
|
|
244
|
+
f"Find invalid key: {rxn}\n"
|
|
245
|
+
f" Missing key of transition state: {key_ts}\n",
|
|
246
|
+
flush=True,
|
|
247
|
+
)
|
|
248
|
+
continue
|
|
249
|
+
elif key_p is not None and key_p not in result.db_minima:
|
|
250
|
+
print(
|
|
251
|
+
f"Find invalid key: {rxn}\n"
|
|
252
|
+
f" Missing key of product: {key_p}\n",
|
|
253
|
+
flush=True,
|
|
254
|
+
)
|
|
255
|
+
continue
|
|
256
|
+
result.metadata.table.merge( # Output duplicate info
|
|
257
|
+
network.metadata.table, idxs=[i]
|
|
258
|
+
)
|
|
259
|
+
n = len(result.metadata.table.key_rxn)
|
|
260
|
+
print(f"Merge networks({n} events) done.", flush=True)
|
|
261
|
+
result.scheduler.clear()
|
|
262
|
+
result.recorder.clear()
|
|
263
|
+
result.persistence()
|
|
264
|
+
return result
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def reformat_message(cls, msg: str) -> str:
|
|
268
|
+
return msg
|
|
269
|
+
__LOGURU_FORMAT_LENGTH: int = 37
|
|
270
|
+
__LOGURU_TOTAL_LENGTH: int = 120
|
|
271
|
+
len_msg = __LOGURU_TOTAL_LENGTH - __LOGURU_FORMAT_LENGTH
|
|
272
|
+
lst = wrap_line(msg, width_min=len_msg, width_max=len_msg + 20)
|
|
273
|
+
return ("\n" + " " * __LOGURU_FORMAT_LENGTH).join(lst)
|
|
274
|
+
|
|
275
|
+
@override
|
|
276
|
+
def __len__(self) -> int:
|
|
277
|
+
return len(self.metadata.table.key_rxn)
|
|
278
|
+
|
|
279
|
+
@override
|
|
280
|
+
def __contains__(self, key: str) -> bool: # type: ignore
|
|
281
|
+
return key in self.metadata.table.key_rxn
|
|
282
|
+
|
|
283
|
+
@override
|
|
284
|
+
def __getitem__(self, key: str) -> tuple[EventInfo, EventBase]:
|
|
285
|
+
return self.read_event(key)
|
|
286
|
+
|
|
287
|
+
@override
|
|
288
|
+
def __iter__(self) -> Iterator[str]:
|
|
289
|
+
yield from self.metadata.table.key_rxn
|
|
290
|
+
|
|
121
291
|
@property
|
|
122
292
|
def path(self) -> Path:
|
|
123
293
|
return self.__path
|
|
@@ -186,76 +356,6 @@ class ReactionNetwork:
|
|
|
186
356
|
)
|
|
187
357
|
return self.__db_ts
|
|
188
358
|
|
|
189
|
-
@classmethod
|
|
190
|
-
def from_config(
|
|
191
|
-
cls,
|
|
192
|
-
config: Config,
|
|
193
|
-
*args,
|
|
194
|
-
restart: bool | None = None,
|
|
195
|
-
remote_mode: bool = False,
|
|
196
|
-
**kwargs,
|
|
197
|
-
) -> Self: # type: ignore
|
|
198
|
-
"""Create a network from a config file."""
|
|
199
|
-
if remote_mode is True:
|
|
200
|
-
# When remote_mode is True, some files may not exist or in write.
|
|
201
|
-
# We need to try to create the network again.
|
|
202
|
-
err = None
|
|
203
|
-
for _ in range(10):
|
|
204
|
-
try:
|
|
205
|
-
return cls(
|
|
206
|
-
path=Path(config.outputs) / NETWORK_PATH_PREFIX,
|
|
207
|
-
restart=restart
|
|
208
|
-
if restart is not None
|
|
209
|
-
else config.restart,
|
|
210
|
-
format=config.event.db_format,
|
|
211
|
-
remote_mode=remote_mode,
|
|
212
|
-
config=config.event,
|
|
213
|
-
**kwargs,
|
|
214
|
-
)
|
|
215
|
-
break
|
|
216
|
-
except Exception as e:
|
|
217
|
-
time.sleep(0.5)
|
|
218
|
-
err = e
|
|
219
|
-
if err is not None:
|
|
220
|
-
raise RuntimeError(
|
|
221
|
-
f"Failed to create the network in remote mode. {err}"
|
|
222
|
-
)
|
|
223
|
-
else:
|
|
224
|
-
return cls(
|
|
225
|
-
path=Path(config.outputs) / NETWORK_PATH_PREFIX,
|
|
226
|
-
restart=restart if restart is not None else config.restart,
|
|
227
|
-
format=config.event.db_format,
|
|
228
|
-
remote_mode=remote_mode,
|
|
229
|
-
config=config.event,
|
|
230
|
-
**kwargs,
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
@classmethod
|
|
234
|
-
def merge(
|
|
235
|
-
cls,
|
|
236
|
-
*networks: Self,
|
|
237
|
-
new_path: Path | str,
|
|
238
|
-
new_format: str | Literal["dir", "h5", "sqlite"] = "dir",
|
|
239
|
-
**kwargs,
|
|
240
|
-
) -> Self:
|
|
241
|
-
"""Merge the networks."""
|
|
242
|
-
raise NotImplementedError("Merge the networks.")
|
|
243
|
-
cls(
|
|
244
|
-
path=new_path,
|
|
245
|
-
restart=False,
|
|
246
|
-
config=networks[0].metadata.basic,
|
|
247
|
-
format=networks[0].__format,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
@classmethod
|
|
251
|
-
def reformat_message(cls, msg: str) -> str:
|
|
252
|
-
return msg
|
|
253
|
-
__LOGURU_FORMAT_LENGTH: int = 37
|
|
254
|
-
__LOGURU_TOTAL_LENGTH: int = 120
|
|
255
|
-
len_msg = __LOGURU_TOTAL_LENGTH - __LOGURU_FORMAT_LENGTH
|
|
256
|
-
lst = wrap_line(msg, width_min=len_msg, width_max=len_msg + 20)
|
|
257
|
-
return ("\n" + " " * __LOGURU_FORMAT_LENGTH).join(lst)
|
|
258
|
-
|
|
259
359
|
def exploration_can_be_finished(
|
|
260
360
|
self,
|
|
261
361
|
confidence: float,
|
|
@@ -311,7 +411,8 @@ class ReactionNetwork:
|
|
|
311
411
|
msg += f" #Minima :{len(self.__db_minima)}\n"
|
|
312
412
|
msg += f" #Gas :{len(self.__db_gas)}\n"
|
|
313
413
|
msg += f" #TS :{len(self.__db_ts)}\n"
|
|
314
|
-
msg += f"#Event
|
|
414
|
+
msg += f"#Event :{len(self.__metadata.table)}\n"
|
|
415
|
+
msg += f"Configuration :{self.__metadata.basic.to_str(indent=4)}\n"
|
|
315
416
|
return msg
|
|
316
417
|
|
|
317
418
|
def persistence(self) -> None:
|
|
@@ -397,6 +498,7 @@ class ReactionNetwork:
|
|
|
397
498
|
*,
|
|
398
499
|
for_system: str | None = None,
|
|
399
500
|
persist: bool = True,
|
|
501
|
+
env_hop: int = 1, # TODO: make this into whole configuration
|
|
400
502
|
**kwargs,
|
|
401
503
|
) -> Literal["new", "old", "fail"] | str:
|
|
402
504
|
"""Record the event as found.
|
|
@@ -425,13 +527,22 @@ class ReactionNetwork:
|
|
|
425
527
|
simplified_threshold = self.__metadata.basic.simplified_threshold
|
|
426
528
|
if simplified_threshold > 0:
|
|
427
529
|
try:
|
|
428
|
-
event = event.simplify(
|
|
530
|
+
event = event.simplify(
|
|
531
|
+
env_radius=simplified_threshold,
|
|
532
|
+
env_hop=env_hop,
|
|
533
|
+
)
|
|
429
534
|
except Exception as e:
|
|
430
535
|
msg = f"Event {event._string()} failed "
|
|
431
536
|
msg += f"to simplify. because of {e}"
|
|
432
537
|
fname = self.__path / "event-simplify-fail.pkl"
|
|
433
|
-
fname.write_bytes(pickle.dumps(event))
|
|
434
538
|
fname.with_suffix(".err").write_text(msg)
|
|
539
|
+
fname.write_bytes(pickle.dumps(event))
|
|
540
|
+
event.R.write_npz(fname.with_suffix(".R.npz"))
|
|
541
|
+
event.P.write_npz(fname.with_suffix(".P.npz"))
|
|
542
|
+
if event.G is not None:
|
|
543
|
+
event.G.write_npz(fname.with_suffix(".G.npz"))
|
|
544
|
+
if event.T is not None:
|
|
545
|
+
event.T.write_npz(fname.with_suffix(".T.npz"))
|
|
435
546
|
raise ValueError(msg)
|
|
436
547
|
if self.write_event(
|
|
437
548
|
event,
|
|
@@ -659,3 +770,31 @@ def wrap_line(
|
|
|
659
770
|
lines.pop()
|
|
660
771
|
|
|
661
772
|
return lines
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
if __name__ == "__main__":
|
|
776
|
+
import json
|
|
777
|
+
import shutil
|
|
778
|
+
from pathlib import Path
|
|
779
|
+
|
|
780
|
+
this_dir = Path(__file__).parent
|
|
781
|
+
root_path = this_dir.parent.parent.parent.parent
|
|
782
|
+
print(root_path)
|
|
783
|
+
|
|
784
|
+
new_path = root_path / "zzzz-merged" / "event"
|
|
785
|
+
shutil.rmtree(new_path.parent, ignore_errors=True)
|
|
786
|
+
|
|
787
|
+
network_lst: list[ReactionNetwork] = []
|
|
788
|
+
for zzzz_path in root_path.glob("zzzz-*"):
|
|
789
|
+
if "merge" not in zzzz_path.name:
|
|
790
|
+
p = zzzz_path / "event"
|
|
791
|
+
(p / "recorder.json").unlink(missing_ok=True)
|
|
792
|
+
with open(p / "recorder.json", "w") as f:
|
|
793
|
+
json.dump({}, f, indent=4)
|
|
794
|
+
network_lst.append(
|
|
795
|
+
ReactionNetwork(
|
|
796
|
+
zzzz_path / "event",
|
|
797
|
+
restart=True,
|
|
798
|
+
)
|
|
799
|
+
)
|
|
800
|
+
new_network = ReactionNetwork.merge(*network_lst, new_path=new_path)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import typer
|
|
6
|
+
from ase import Atoms
|
|
7
|
+
from ase.visualize import view as _ase_view
|
|
8
|
+
|
|
9
|
+
from graphatoms.enterpoint.network import ReactionNetwork as Net
|
|
10
|
+
from graphatoms.system import SysGraph
|
|
11
|
+
|
|
12
|
+
__p_help__ = "Path to the network directory."
|
|
13
|
+
Path = Annotated[str, typer.Option("--path", "-p", help=__p_help__)]
|
|
14
|
+
pd.set_option("display.max_colwidth", None) # don't truncate cell contents
|
|
15
|
+
pd.set_option("display.max_columns", None) # show all columns
|
|
16
|
+
pd.set_option("display.max_rows", None) # show all rows
|
|
17
|
+
pd.set_option("display.width", None) # don't wrap lines
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# --------------
|
|
21
|
+
# Main command
|
|
22
|
+
# --------------
|
|
23
|
+
main = typer.Typer(no_args_is_help=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@main.command()
|
|
27
|
+
def summary(path: Path = ".") -> None:
|
|
28
|
+
assert pathlib.Path(path).exists()
|
|
29
|
+
net = Net(path, restart=True, format="dir")
|
|
30
|
+
print(net.summary())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@main.command(no_args_is_help=True)
|
|
34
|
+
def merge(
|
|
35
|
+
args: Annotated[
|
|
36
|
+
list[str],
|
|
37
|
+
typer.Argument(
|
|
38
|
+
help=f"{Net.__name__} directories to merge (one or more)"
|
|
39
|
+
),
|
|
40
|
+
],
|
|
41
|
+
path: Path = "./event",
|
|
42
|
+
db_format: Annotated[
|
|
43
|
+
str,
|
|
44
|
+
typer.Option(
|
|
45
|
+
"--db-format",
|
|
46
|
+
"-f",
|
|
47
|
+
help="Database format in the network.",
|
|
48
|
+
),
|
|
49
|
+
] = "dir",
|
|
50
|
+
) -> None:
|
|
51
|
+
assert db_format == "dir", "Only `dir` format is supported now."
|
|
52
|
+
lst = [Net(p, restart=True, format=db_format) for p in args]
|
|
53
|
+
Net.merge(*lst, new_path=path)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ----------------
|
|
57
|
+
# Table subcommand
|
|
58
|
+
# ----------------
|
|
59
|
+
show = typer.Typer(no_args_is_help=True)
|
|
60
|
+
main.add_typer(show, name="show")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@show.command(name="reaction")
|
|
64
|
+
def show_reaction(arg: str, path: Path = ".") -> tuple[int, Net]:
|
|
65
|
+
net = Net(path, restart=True, format="dir")
|
|
66
|
+
df = net.metadata.table.dataframe
|
|
67
|
+
try:
|
|
68
|
+
i = int(arg)
|
|
69
|
+
except ValueError:
|
|
70
|
+
i = net.metadata.table.key_rxn.index(arg)
|
|
71
|
+
print(df.iloc[i])
|
|
72
|
+
return i, net
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@show.command()
|
|
76
|
+
def table(path: Path = ".") -> None:
|
|
77
|
+
assert pathlib.Path(path).exists()
|
|
78
|
+
net = Net(path, restart=True, format="dir")
|
|
79
|
+
df = net.metadata.table.dataframe
|
|
80
|
+
col = [0, 5, 6, 7, 8, 11, 12]
|
|
81
|
+
print(df[df.columns[col]])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ----------------
|
|
85
|
+
# View subcommand
|
|
86
|
+
# ----------------
|
|
87
|
+
view = typer.Typer(no_args_is_help=True)
|
|
88
|
+
|
|
89
|
+
main.add_typer(view, name="view")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@view.command(name="reaction")
|
|
93
|
+
def view_reaction(
|
|
94
|
+
name: str,
|
|
95
|
+
path: Path = ".",
|
|
96
|
+
forward: bool = True,
|
|
97
|
+
) -> None:
|
|
98
|
+
idx, net = show_reaction(name, path)
|
|
99
|
+
key_rxn = net.metadata.table.key_rxn[idx]
|
|
100
|
+
_, rxn = net.read_event(key_rxn)
|
|
101
|
+
if not forward:
|
|
102
|
+
rxn = rxn.reversed
|
|
103
|
+
return rxn.view()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@view.command(name="structure")
|
|
107
|
+
def view_structure(
|
|
108
|
+
name_or_path: str,
|
|
109
|
+
path: Path = ".",
|
|
110
|
+
) -> None:
|
|
111
|
+
try:
|
|
112
|
+
fname = pathlib.Path(name_or_path)
|
|
113
|
+
if fname.name.count("*") == 0:
|
|
114
|
+
assert fname.exists() and fname.is_file()
|
|
115
|
+
return _ase_view(SysGraph.read_npz(fname).to_ase())
|
|
116
|
+
else:
|
|
117
|
+
return _ase_view(
|
|
118
|
+
[
|
|
119
|
+
SysGraph.read_npz(i).to_ase()
|
|
120
|
+
for i in fname.parent.glob(fname.name)
|
|
121
|
+
]
|
|
122
|
+
)
|
|
123
|
+
except ValueError:
|
|
124
|
+
name = name_or_path
|
|
125
|
+
|
|
126
|
+
assert pathlib.Path(path).exists()
|
|
127
|
+
net = Net(path, restart=True, format="dir")
|
|
128
|
+
atoms: Atoms | None = None
|
|
129
|
+
for type in ("gas", "ts", "minima", "cluster", "system"):
|
|
130
|
+
if net.contain_sysgraph(name, type=type):
|
|
131
|
+
atoms = net.read_atoms(name, type=type)
|
|
132
|
+
break
|
|
133
|
+
if atoms is None:
|
|
134
|
+
raise ValueError(f"structure {name} is not in the database.")
|
|
135
|
+
return _ase_view(atoms)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
main()
|
|
@@ -21,12 +21,23 @@ class _MetaDataBasic(OurBaseModel, EventConfig):
|
|
|
21
21
|
def from_config(cls, config: Config | EventConfig) -> Self:
|
|
22
22
|
if isinstance(config, Config):
|
|
23
23
|
config = config.event
|
|
24
|
+
if not isinstance(config, dict):
|
|
25
|
+
if isinstance(config, EventConfig):
|
|
26
|
+
config = dc.asdict(config) # type: ignore
|
|
27
|
+
elif isinstance(config, _MetaDataBasic):
|
|
28
|
+
config = config.to_dict()
|
|
24
29
|
return cls(**config) # type: ignore
|
|
25
30
|
|
|
26
|
-
def check_consistency(
|
|
31
|
+
def check_consistency(
|
|
32
|
+
self,
|
|
33
|
+
config: EventConfig | Self,
|
|
34
|
+
exclude_keys: set[str] = {"db_format"},
|
|
35
|
+
) -> None:
|
|
27
36
|
msg = r"The keys of {} must be the same as input, "
|
|
28
37
|
msg += r"but we got {}(from self) and {}(from input)"
|
|
29
38
|
for k in self.__pydantic_fields__:
|
|
39
|
+
if k in exclude_keys:
|
|
40
|
+
continue
|
|
30
41
|
v1 = getattr(self, k)
|
|
31
42
|
v2 = getattr(config, k)
|
|
32
43
|
if not isinstance(v1, dict):
|
|
@@ -99,6 +110,41 @@ class _MetaDataTable(BaseModel):
|
|
|
99
110
|
def from_dataframe(cls, df: pd.DataFrame) -> Self:
|
|
100
111
|
return cls(**{k: df[k].to_list() for k in df.columns})
|
|
101
112
|
|
|
113
|
+
def merge(self, other: Self, idxs: list[int] | None = None) -> None:
|
|
114
|
+
if idxs is None:
|
|
115
|
+
idxs = list(range(len(other)))
|
|
116
|
+
for i in idxs:
|
|
117
|
+
k = other.key_rxn[i]
|
|
118
|
+
if k not in self.key_rxn:
|
|
119
|
+
self.key_rxn.append(k)
|
|
120
|
+
self.key_r.append(other.key_r[i])
|
|
121
|
+
self.key_g.append(other.key_g[i])
|
|
122
|
+
self.key_t.append(other.key_t[i])
|
|
123
|
+
self.key_p.append(other.key_p[i])
|
|
124
|
+
self.Ea_forword.append(other.Ea_forword[i])
|
|
125
|
+
self.rate_forword.append(other.rate_forword[i])
|
|
126
|
+
self.rate_reversed.append(other.rate_reversed[i])
|
|
127
|
+
self.Ea_reversed.append(other.Ea_reversed[i])
|
|
128
|
+
self.for_cluster.append(other.for_cluster[i])
|
|
129
|
+
self.for_system.append(other.for_system[i])
|
|
130
|
+
self.count.append(other.count[i])
|
|
131
|
+
self.dE.append(other.dE[i])
|
|
132
|
+
else:
|
|
133
|
+
idx = self.key_rxn.index(k)
|
|
134
|
+
self.count[idx] += other.count[i]
|
|
135
|
+
len(self.key_r[idx])
|
|
136
|
+
print(
|
|
137
|
+
f"Find duplicate key: {k}\n"
|
|
138
|
+
f" dE: (old={self.dE[idx]},new={other.dE[i]})\n"
|
|
139
|
+
f" Ea: (old={self.Ea_forword[idx]},"
|
|
140
|
+
f"new={other.Ea_forword[i]})\n"
|
|
141
|
+
f" key_r:(old={self.key_r[idx]},new={other.key_r[i]})\n"
|
|
142
|
+
f" key_g:(old={self.key_g[idx]},new={other.key_g[i]})\n"
|
|
143
|
+
f" key_t:(old={self.key_t[idx]},new={other.key_t[i]})\n"
|
|
144
|
+
f" key_p:(old={self.key_p[idx]},new={other.key_p[i]})\n",
|
|
145
|
+
flush=True,
|
|
146
|
+
)
|
|
147
|
+
|
|
102
148
|
def get_minconut_for(
|
|
103
149
|
self,
|
|
104
150
|
*,
|
|
@@ -22,6 +22,7 @@ Ref:
|
|
|
22
22
|
Fe-H system. PhD thesis, University of Cambridge, 2024.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
+
from abc import abstractmethod
|
|
25
26
|
from collections import defaultdict
|
|
26
27
|
from datetime import datetime
|
|
27
28
|
from typing import Annotated, Any, Self, override
|
|
@@ -180,16 +181,32 @@ class RecorderBase(OurBaseModel):
|
|
|
180
181
|
+ "have been explored"
|
|
181
182
|
)
|
|
182
183
|
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def clear(self) -> None:
|
|
186
|
+
self.cluster.clear()
|
|
187
|
+
|
|
183
188
|
|
|
184
189
|
class Recorder(RecorderBase):
|
|
185
190
|
adsorption: set[str] = set()
|
|
186
191
|
bulk: set[str] = set()
|
|
187
192
|
|
|
193
|
+
@override
|
|
194
|
+
def clear(self) -> None:
|
|
195
|
+
self.adsorption.clear()
|
|
196
|
+
self.bulk.clear()
|
|
197
|
+
super().clear()
|
|
198
|
+
|
|
188
199
|
|
|
189
200
|
class RecorderV2(RecorderBase):
|
|
190
201
|
adsorption: dict[tuple[str, str], set[str]] = defaultdict(set)
|
|
191
202
|
bulk: dict[str, set[str]] = defaultdict(set)
|
|
192
203
|
|
|
204
|
+
@override
|
|
205
|
+
def clear(self) -> None:
|
|
206
|
+
self.adsorption.clear()
|
|
207
|
+
self.bulk.clear()
|
|
208
|
+
super().clear()
|
|
209
|
+
|
|
193
210
|
def exploration_can_be_finished(
|
|
194
211
|
self,
|
|
195
212
|
cluster_key: str,
|
|
@@ -20,6 +20,10 @@ class Scheduler(BaseModel):
|
|
|
20
20
|
msg = " ".join(f"{k}:{len(v)}dR" for k, v in self.dR.items())
|
|
21
21
|
return f"{self.__class__.__name__}({msg})"
|
|
22
22
|
|
|
23
|
+
def clear(self) -> None:
|
|
24
|
+
self.is_fixed.clear()
|
|
25
|
+
self.dR.clear()
|
|
26
|
+
|
|
23
27
|
@validate_call
|
|
24
28
|
def write_npz(
|
|
25
29
|
self,
|