GraphAtoms 2.1.0__tar.gz → 2.1.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.1.0 → graphatoms-2.1.1}/PKG-INFO +1 -1
- {graphatoms-2.1.0 → graphatoms-2.1.1}/pyproject.toml +1 -1
- {graphatoms-2.1.0 → graphatoms-2.1.1}/pyproject.toml.orig +1 -1
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/__init__.py +1 -4
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/network/__init__.py +51 -23
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/network/_metadata.py +3 -1
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/network/_recorder.py +84 -6
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/__init__.py +32 -0
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/common/__init__.py +21 -0
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/common/_base.py +262 -0
- {graphatoms-2.1.0/src/graphatoms/enterpoint/runner → graphatoms-2.1.1/src/graphatoms/enterpoint/runner/common}/_helper.py +7 -7
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/general/__init__.py +0 -0
- graphatoms-2.1.0/src/graphatoms/enterpoint/runner/_base.py → graphatoms-2.1.1/src/graphatoms/enterpoint/runner/general/_expl.py +13 -258
- {graphatoms-2.1.0/src/graphatoms/enterpoint/runner → graphatoms-2.1.1/src/graphatoms/enterpoint/runner/general}/otfkmc.py +12 -6
- {graphatoms-2.1.0/src/graphatoms/enterpoint/runner → graphatoms-2.1.1/src/graphatoms/enterpoint/runner/general}/rxngen.py +2 -2
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/ray/__init__.py +0 -0
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/ray/_expl.py +36 -0
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/ray/otfkmc.py +9 -0
- graphatoms-2.1.1/src/graphatoms/enterpoint/runner/ray/rxngen.py +5 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/view.py +4 -4
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/bonds.py +20 -13
- graphatoms-2.1.0/src/graphatoms/enterpoint/runner/__init__.py +0 -24
- {graphatoms-2.1.0 → graphatoms-2.1.1}/README.md +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/LICENSE +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/arrayapi/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/arrayapi/compat.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/arrayapi/typing.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/dataclasses/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/dataclasses/_numpydantic.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/dataclasses/_pydantic2pyarrow.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/dataclasses/_pydanticMixin.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/dataclasses/_pydanticModel.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/dataclasses/graph.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/atoms/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/atoms/aseread.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/atoms/octahedron.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/bonds/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/bonds/raw.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/calculator/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/calculator/emt.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/calculator/nequip.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/example.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/config/run.yaml +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/network/_scheduler.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/parallel/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/parallel/_base.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/parallel/_dask.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/parallel/_executorlib.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/enterpoint/parallel/_ray.py +0 -0
- {graphatoms-2.1.0/src/graphatoms/enterpoint/runner → graphatoms-2.1.1/src/graphatoms/enterpoint/runner/common}/_errors.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/_bond_list.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/_distance_pairs.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/_inner_outer.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/_neighbor_list.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/mic.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/rotation.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/geometry/sample.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/reaction/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/reaction/_event.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/reaction/reaction.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/reaction/xxsorption.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/atoms.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/database/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/database/abc.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/database/dict.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/database/dir.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/database/hdf5.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/database/sqliteASE.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/graph.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/sysCluster.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/sysGas.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/system/system.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/__init__.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/adsorption.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/asetools.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/bytestool.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/logger.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/parser.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/rdutils.py +0 -0
- {graphatoms-2.1.0 → graphatoms-2.1.1}/src/graphatoms/utils/subgraph.py +0 -0
|
@@ -12,7 +12,7 @@ graphatoms-network = "graphatoms.enterpoint.view:main"
|
|
|
12
12
|
graphatoms-run = "graphatoms.enterpoint.runner:run"
|
|
13
13
|
[project]
|
|
14
14
|
name = "graphatoms"
|
|
15
|
-
version = "2.1.
|
|
15
|
+
version = "2.1.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" }]
|
|
@@ -66,7 +66,7 @@ class Config:
|
|
|
66
66
|
|
|
67
67
|
calculator: CalcConfig = MISSING
|
|
68
68
|
|
|
69
|
-
outputs: str = "
|
|
69
|
+
outputs: str = "."
|
|
70
70
|
max_steps: int = 100000
|
|
71
71
|
max_times: float = 1000.0 # seconds
|
|
72
72
|
run_type: str = "otfkmc" # otfkmc, rxngen
|
|
@@ -98,8 +98,6 @@ cs.store(group="calculator", name="calc_nequip", node=NequipConfig)
|
|
|
98
98
|
version_base=None,
|
|
99
99
|
)
|
|
100
100
|
def print_config(cfg: Config) -> None:
|
|
101
|
-
from shutil import rmtree
|
|
102
|
-
|
|
103
101
|
print(OmegaConf.to_yaml(cfg))
|
|
104
102
|
print("""
|
|
105
103
|
hydra:
|
|
@@ -107,4 +105,3 @@ hydra:
|
|
|
107
105
|
dir: ${outputs}
|
|
108
106
|
verbose: ${debug}
|
|
109
107
|
""")
|
|
110
|
-
rmtree(Path(cfg.outputs))
|
|
@@ -4,8 +4,14 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Literal
|
|
5
5
|
|
|
6
6
|
from graphatoms.enterpoint.config import EventConfig
|
|
7
|
-
from graphatoms.reaction import
|
|
8
|
-
|
|
7
|
+
from graphatoms.reaction import (
|
|
8
|
+
Adsorption,
|
|
9
|
+
Desorption,
|
|
10
|
+
EventBase,
|
|
11
|
+
EventInfo,
|
|
12
|
+
Reaction,
|
|
13
|
+
)
|
|
14
|
+
from graphatoms.system import Gas, SysGraph
|
|
9
15
|
from graphatoms.system.database import DatabaseABC, get_db
|
|
10
16
|
|
|
11
17
|
from ._metadata import MetaData
|
|
@@ -14,14 +20,14 @@ from ._recorder import Recorder
|
|
|
14
20
|
from ._scheduler import Scheduler
|
|
15
21
|
|
|
16
22
|
__all__ = [
|
|
17
|
-
"RxNet",
|
|
18
23
|
"MetaData",
|
|
19
24
|
"Recorder",
|
|
20
25
|
"Scheduler",
|
|
26
|
+
"ReactionNetwork",
|
|
21
27
|
]
|
|
22
28
|
|
|
23
29
|
|
|
24
|
-
class
|
|
30
|
+
class ReactionNetwork:
|
|
25
31
|
def __init__(
|
|
26
32
|
self,
|
|
27
33
|
path: Path | str,
|
|
@@ -81,16 +87,26 @@ class RxNet:
|
|
|
81
87
|
self.metadata: MetaData = MetaData()
|
|
82
88
|
|
|
83
89
|
# initialize the databases
|
|
84
|
-
|
|
90
|
+
(
|
|
91
|
+
self.db_ts,
|
|
92
|
+
self.db_gas,
|
|
93
|
+
self.db_minima,
|
|
94
|
+
self.db_cluster,
|
|
95
|
+
self.db_system,
|
|
96
|
+
) = [
|
|
85
97
|
get_db(
|
|
86
98
|
path=path,
|
|
87
99
|
format=format,
|
|
88
100
|
prefix=prefix,
|
|
89
101
|
append=restart,
|
|
90
102
|
)
|
|
91
|
-
for prefix in ["ts", "gas", "minima"]
|
|
103
|
+
for prefix in ["ts", "gas", "minima", "cluster", "system"]
|
|
92
104
|
]
|
|
93
|
-
self.db_ts,
|
|
105
|
+
assert isinstance(self.db_ts, DatabaseABC)
|
|
106
|
+
assert isinstance(self.db_gas, DatabaseABC)
|
|
107
|
+
assert isinstance(self.db_minima, DatabaseABC)
|
|
108
|
+
assert isinstance(self.db_cluster, DatabaseABC)
|
|
109
|
+
assert isinstance(self.db_system, DatabaseABC)
|
|
94
110
|
|
|
95
111
|
def summary(self) -> str:
|
|
96
112
|
"""Return the summary of the network."""
|
|
@@ -102,16 +118,24 @@ class RxNet:
|
|
|
102
118
|
self.scheduler.write_npz(self.__path / "scheduler.npz")
|
|
103
119
|
self.metadata.persistence(self.__path)
|
|
104
120
|
|
|
105
|
-
def
|
|
121
|
+
def read_event(self, key: str) -> tuple[EventInfo, EventBase]:
|
|
122
|
+
"""Read the event from the database."""
|
|
106
123
|
if self.metadata.has(key):
|
|
107
124
|
info = self.metadata.read(key)
|
|
108
125
|
if info.key_g is not None:
|
|
109
|
-
|
|
126
|
+
assert info.key_t is None
|
|
127
|
+
g = Gas.from_ase(self.db_gas[info.key_g])
|
|
128
|
+
r = SysGraph.from_ase(self.db_minima[info.key_r])
|
|
129
|
+
p = SysGraph.from_ase(self.db_minima[info.key_p])
|
|
130
|
+
if r.natoms < p.natoms:
|
|
131
|
+
return info, Adsorption(G=g, R=r, P=p)
|
|
132
|
+
else:
|
|
133
|
+
return info, Desorption(G=g, R=r, P=p)
|
|
110
134
|
else:
|
|
111
135
|
assert info.key_t is not None
|
|
112
|
-
ts =
|
|
113
|
-
r =
|
|
114
|
-
p =
|
|
136
|
+
ts = SysGraph.from_ase(self.db_ts[info.key_t])
|
|
137
|
+
r = SysGraph.from_ase(self.db_minima[info.key_r])
|
|
138
|
+
p = SysGraph.from_ase(self.db_minima[info.key_p])
|
|
115
139
|
return info, Reaction(T=ts, R=r, P=p)
|
|
116
140
|
else:
|
|
117
141
|
raise ValueError(f"Event {key} is not in the database.")
|
|
@@ -143,7 +167,7 @@ class RxNet:
|
|
|
143
167
|
fname.write_bytes(pickle.dumps(event))
|
|
144
168
|
fname.with_suffix(".err").write_text(msg)
|
|
145
169
|
raise ValueError(msg)
|
|
146
|
-
is_new = self.
|
|
170
|
+
is_new = self.write_event(
|
|
147
171
|
event,
|
|
148
172
|
for_cluster,
|
|
149
173
|
for_system,
|
|
@@ -154,7 +178,7 @@ class RxNet:
|
|
|
154
178
|
else:
|
|
155
179
|
raise ValueError(f"Unknown event type: {type(event)}")
|
|
156
180
|
|
|
157
|
-
def
|
|
181
|
+
def write_event(
|
|
158
182
|
self,
|
|
159
183
|
event: EventBase,
|
|
160
184
|
for_cluster: str,
|
|
@@ -170,12 +194,12 @@ class RxNet:
|
|
|
170
194
|
else:
|
|
171
195
|
self.metadata.write(event, for_cluster, for_system)
|
|
172
196
|
try:
|
|
173
|
-
self.
|
|
174
|
-
self.
|
|
197
|
+
self.write_sysgraph(event.R, "minima")
|
|
198
|
+
self.write_sysgraph(event.P, "minima")
|
|
175
199
|
if event.G is not None:
|
|
176
|
-
self.
|
|
200
|
+
self.write_sysgraph(event.G, "gas")
|
|
177
201
|
if event.T is not None:
|
|
178
|
-
self.
|
|
202
|
+
self.write_sysgraph(event.T, "ts")
|
|
179
203
|
if persist:
|
|
180
204
|
self.persistence()
|
|
181
205
|
return True
|
|
@@ -201,17 +225,21 @@ class RxNet:
|
|
|
201
225
|
self.persistence()
|
|
202
226
|
raise e
|
|
203
227
|
|
|
204
|
-
def
|
|
228
|
+
def write_sysgraph(
|
|
205
229
|
self,
|
|
206
230
|
sysgraph: SysGraph,
|
|
207
|
-
type: Literal["minima", "ts", "gas"] | str,
|
|
231
|
+
type: Literal["minima", "ts", "gas", "cluster", "system"] | str,
|
|
208
232
|
) -> bool:
|
|
209
233
|
"""Return True if the value is new, False otherwise."""
|
|
210
|
-
if type == "minima":
|
|
234
|
+
if type.lower() == "minima":
|
|
211
235
|
return self.db_minima.add(sysgraph, check_positions=True)
|
|
212
|
-
elif type == "ts":
|
|
236
|
+
elif type.lower() == "ts":
|
|
213
237
|
return self.db_ts.add(sysgraph, check_positions=True)
|
|
214
|
-
elif type == "gas":
|
|
238
|
+
elif type.lower() == "gas":
|
|
215
239
|
return self.db_gas.add(sysgraph, check_positions=False)
|
|
240
|
+
elif type.lower() == "system":
|
|
241
|
+
return self.db_system.add(sysgraph, check_positions=True)
|
|
242
|
+
elif type.lower() == "cluster":
|
|
243
|
+
return self.db_cluster.add(sysgraph, check_positions=True)
|
|
216
244
|
else:
|
|
217
245
|
raise ValueError(f"Unknown type: {type}")
|
|
@@ -14,6 +14,7 @@ from pydantic import (
|
|
|
14
14
|
from graphatoms.dataclasses import OurBaseModel
|
|
15
15
|
from graphatoms.enterpoint.config import EventConfig
|
|
16
16
|
from graphatoms.reaction import EventBase, EventInfo
|
|
17
|
+
from graphatoms.system.bonds import matchmode2nmatch
|
|
17
18
|
|
|
18
19
|
__all__ = ["MetaData"]
|
|
19
20
|
|
|
@@ -181,7 +182,8 @@ class MetaData(BaseModel):
|
|
|
181
182
|
names = ["rxn_key", "is_forward", "nmatched", "rate"]
|
|
182
183
|
for (rxn_key, is_forward), single_match_res in matching_dct.items():
|
|
183
184
|
i: int = self.table.key_rxn.index(rxn_key)
|
|
184
|
-
|
|
185
|
+
# Eliminate the overestimation caused by permutation symmetry.
|
|
186
|
+
nmatched = matchmode2nmatch(single_match_res)
|
|
185
187
|
if is_forward:
|
|
186
188
|
rate = self.table.rate_forword[i]
|
|
187
189
|
else:
|
|
@@ -23,14 +23,15 @@ Ref:
|
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
from collections import defaultdict
|
|
26
|
-
from
|
|
26
|
+
from datetime import datetime
|
|
27
|
+
from typing import Annotated, override
|
|
27
28
|
|
|
28
29
|
import pydantic
|
|
29
30
|
|
|
30
31
|
from graphatoms.dataclasses import OurBaseModel # type: ignore
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
class
|
|
34
|
+
class _RecorderBase(pydantic.BaseModel):
|
|
34
35
|
old: pydantic.NonNegativeInt = 0
|
|
35
36
|
new: pydantic.NonNegativeInt = 0
|
|
36
37
|
fail: pydantic.NonNegativeInt = 0
|
|
@@ -47,6 +48,70 @@ class _OldNewRecorder(OurBaseModel):
|
|
|
47
48
|
"""
|
|
48
49
|
return sum([self.old, self.new, self.fail])
|
|
49
50
|
|
|
51
|
+
class RecorderInfo(_RecorderBase):
|
|
52
|
+
create_at: datetime = pydantic.Field(default_factory=lambda: datetime.now())
|
|
53
|
+
confidence: Annotated[float, pydantic.Field(ge=0.5, lt=1.0)]
|
|
54
|
+
for_cluster: str = pydantic.Field(default="")
|
|
55
|
+
for_system: str = pydantic.Field(default="")
|
|
56
|
+
for_gas: str = pydantic.Field(default="")
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def get_csv_title(cls) -> str:
|
|
60
|
+
return ",".join(
|
|
61
|
+
[
|
|
62
|
+
"old",
|
|
63
|
+
"new",
|
|
64
|
+
"fail",
|
|
65
|
+
"total",
|
|
66
|
+
"skip",
|
|
67
|
+
"continuous_old",
|
|
68
|
+
"continuous_nonnew",
|
|
69
|
+
"confidence",
|
|
70
|
+
"create_at",
|
|
71
|
+
"for_cluster",
|
|
72
|
+
"for_system",
|
|
73
|
+
"for_gas",
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def to_csv_line(self) -> str:
|
|
78
|
+
return ",".join(
|
|
79
|
+
[
|
|
80
|
+
f"{self.old}",
|
|
81
|
+
f"{self.new}",
|
|
82
|
+
f"{self.fail}",
|
|
83
|
+
f"{self.total}",
|
|
84
|
+
f"{self.skip}",
|
|
85
|
+
f"{self.continuous_old}",
|
|
86
|
+
f"{self.continuous_nonnew}",
|
|
87
|
+
f"{self.confidence}",
|
|
88
|
+
f"{self.create_at.strftime('%Y-%m-%d %H:%M:%S.%f')}",
|
|
89
|
+
f"{self.for_cluster}",
|
|
90
|
+
f"{self.for_system}",
|
|
91
|
+
f"{self.for_gas}",
|
|
92
|
+
]
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class _OldNewRecorder(OurBaseModel, _RecorderBase):
|
|
97
|
+
@pydantic.validate_call
|
|
98
|
+
def get_williams_confidence(
|
|
99
|
+
self, min_found: pydantic.NonNegativeInt
|
|
100
|
+
) -> Annotated[float, pydantic.Field(ge=0.5, lt=1.0)]:
|
|
101
|
+
"""The Williams confidence formula.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
min_found: The minimum time found for all events.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
The confidence value.
|
|
108
|
+
"""
|
|
109
|
+
m = self.continuous_nonnew
|
|
110
|
+
factor = min_found / self.total
|
|
111
|
+
factor2 = 1 / (1 + factor)
|
|
112
|
+
value = 1 / (1 + factor2**m)
|
|
113
|
+
return value
|
|
114
|
+
|
|
50
115
|
@pydantic.validate_call
|
|
51
116
|
def exploration_can_be_finished(
|
|
52
117
|
self,
|
|
@@ -61,10 +126,7 @@ class _OldNewRecorder(OurBaseModel):
|
|
|
61
126
|
"min_found must be provided for Williams"
|
|
62
127
|
+ " formula (i.e. confidence < 1)."
|
|
63
128
|
)
|
|
64
|
-
|
|
65
|
-
factor = min_found / self.total
|
|
66
|
-
factor2 = 1 / (1 + factor)
|
|
67
|
-
value = 1 / (1 + factor2**m)
|
|
129
|
+
value = self.get_williams_confidence(min_found)
|
|
68
130
|
else:
|
|
69
131
|
# Use Xu-Henkelman formula
|
|
70
132
|
value = self.continuous_old
|
|
@@ -119,8 +181,10 @@ class Recorder(OurBaseModel):
|
|
|
119
181
|
|
|
120
182
|
|
|
121
183
|
if __name__ == "__main__":
|
|
184
|
+
from io import StringIO
|
|
122
185
|
from pathlib import Path
|
|
123
186
|
|
|
187
|
+
import pandas as pd
|
|
124
188
|
obj = Recorder()
|
|
125
189
|
obj.system.add("fdsafs")
|
|
126
190
|
obj.cluster["fdsafs"].new += 1
|
|
@@ -128,3 +192,17 @@ if __name__ == "__main__":
|
|
|
128
192
|
print(obj)
|
|
129
193
|
print(repr(obj))
|
|
130
194
|
obj.write_json(Path("a.json"))
|
|
195
|
+
|
|
196
|
+
io = StringIO()
|
|
197
|
+
io.write(RecorderInfo.get_csv_title() + "\n")
|
|
198
|
+
a = RecorderInfo(confidence=0.6)
|
|
199
|
+
io.write(RecorderInfo(confidence=0.5).to_csv_line() + "\n")
|
|
200
|
+
io.write(RecorderInfo(confidence=0.5).to_csv_line() + "\n")
|
|
201
|
+
io.write(a.to_csv_line() + "\n")
|
|
202
|
+
|
|
203
|
+
s = io.getvalue()
|
|
204
|
+
print(s)
|
|
205
|
+
df = pd.read_csv(StringIO(s))
|
|
206
|
+
print("df:")
|
|
207
|
+
print(df)
|
|
208
|
+
print(df.sort_values(by="create_at", ascending=True))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
os.environ["HYDRA_FULL_ERROR"] = "1"
|
|
4
|
+
|
|
5
|
+
import hydra
|
|
6
|
+
|
|
7
|
+
from graphatoms.enterpoint.config import CONFIG_DIR, Config
|
|
8
|
+
|
|
9
|
+
from .general.otfkmc import OTFKMC as OnTheFlyKMC
|
|
10
|
+
from .general.rxngen import ReactionNetworkGenerator
|
|
11
|
+
from .ray.otfkmc import RayOTFKMC as RayOnTheFlyKMC
|
|
12
|
+
from .ray.rxngen import RayReactionNetworkGenerator
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@hydra.main(
|
|
16
|
+
config_path=CONFIG_DIR.as_posix(),
|
|
17
|
+
config_name="run",
|
|
18
|
+
version_base=None,
|
|
19
|
+
)
|
|
20
|
+
def run(cfg: Config) -> None:
|
|
21
|
+
if str(cfg.run_type).lower() == "otfkmc":
|
|
22
|
+
if str(cfg.parallel).lower() == "ray":
|
|
23
|
+
RayOnTheFlyKMC(config=cfg).run()
|
|
24
|
+
else:
|
|
25
|
+
OnTheFlyKMC(config=cfg).run()
|
|
26
|
+
elif str(cfg.run_type).lower() == "rxngen":
|
|
27
|
+
if str(cfg.parallel).lower() == "ray":
|
|
28
|
+
RayReactionNetworkGenerator(config=cfg).run()
|
|
29
|
+
else:
|
|
30
|
+
ReactionNetworkGenerator(config=cfg).run()
|
|
31
|
+
else:
|
|
32
|
+
raise ValueError(f" run_type `{cfg.run_type}` is not supported.")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from ._base import RunnerABC
|
|
2
|
+
from ._errors import CheckVibrationFailed, HelperException, OptimizationFailed
|
|
3
|
+
from ._helper import (
|
|
4
|
+
helper_adsorption,
|
|
5
|
+
helper_apply,
|
|
6
|
+
helper_dimer,
|
|
7
|
+
helper_match,
|
|
8
|
+
helper_optimization,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"RunnerABC",
|
|
13
|
+
"CheckVibrationFailed",
|
|
14
|
+
"HelperException",
|
|
15
|
+
"OptimizationFailed",
|
|
16
|
+
"helper_adsorption",
|
|
17
|
+
"helper_apply",
|
|
18
|
+
"helper_dimer",
|
|
19
|
+
"helper_match",
|
|
20
|
+
"helper_optimization",
|
|
21
|
+
]
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
os.environ["LOGURU_FORMAT"] = LOGURU_FORMAT = (
|
|
8
|
+
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green>"
|
|
9
|
+
+ " | <level>{level: ^8}</level> | "
|
|
10
|
+
+ "<level>{message}</level>"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
import hydra
|
|
14
|
+
import numpy as np
|
|
15
|
+
from ase import Atoms
|
|
16
|
+
from loguru._logger import Core, Logger
|
|
17
|
+
from omegaconf import DictConfig, OmegaConf
|
|
18
|
+
|
|
19
|
+
from graphatoms.enterpoint.config import Config
|
|
20
|
+
from graphatoms.enterpoint.network import ReactionNetwork
|
|
21
|
+
from graphatoms.enterpoint.parallel import get_executor
|
|
22
|
+
from graphatoms.system import ( # type: ignore # type: ignore
|
|
23
|
+
Gas,
|
|
24
|
+
System,
|
|
25
|
+
)
|
|
26
|
+
from graphatoms.utils.parser import hydra_parse
|
|
27
|
+
|
|
28
|
+
from ._helper import helper_optimization
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RunnerABC:
|
|
32
|
+
"""The base class for all classes.
|
|
33
|
+
|
|
34
|
+
It provides:
|
|
35
|
+
1. basic configuration (omegaconf.DictConfig)
|
|
36
|
+
2. output directory (pathlib.Path)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
__LOGURU_FORMAT_LENGTH: int = 37
|
|
40
|
+
__LOGURU_TOTAL_LENGTH: int = 120
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def _reformat_message(cls, msg: str) -> str:
|
|
44
|
+
return msg
|
|
45
|
+
len_msg = cls.__LOGURU_TOTAL_LENGTH - cls.__LOGURU_FORMAT_LENGTH
|
|
46
|
+
lst = wrap_line(msg, width_min=len_msg, width_max=len_msg + 20)
|
|
47
|
+
return ("\n" + " " * cls.__LOGURU_FORMAT_LENGTH).join(lst)
|
|
48
|
+
|
|
49
|
+
def __init__(self, *, config: Config) -> None:
|
|
50
|
+
assert isinstance(config, DictConfig | Config)
|
|
51
|
+
self.config: Config = config
|
|
52
|
+
self.path = Path(config.outputs)
|
|
53
|
+
self.path.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
|
|
55
|
+
# Logger Configuration
|
|
56
|
+
loglevel = str(config.loglevel).upper()
|
|
57
|
+
try:
|
|
58
|
+
hydracfg = hydra.core.hydra_config.HydraConfig.get() # type: ignore
|
|
59
|
+
outlogfile = str(hydracfg.job_logging.handlers.file.filename)
|
|
60
|
+
except Exception:
|
|
61
|
+
outlogfile = hydracfg = None
|
|
62
|
+
outlogfile = config.logfile
|
|
63
|
+
assert outlogfile is not None
|
|
64
|
+
outlogfile = str(outlogfile)
|
|
65
|
+
|
|
66
|
+
# Logger Setting
|
|
67
|
+
self.logger = log = Logger(
|
|
68
|
+
core=Core(),
|
|
69
|
+
exception=None,
|
|
70
|
+
depth=0,
|
|
71
|
+
record=False,
|
|
72
|
+
lazy=False,
|
|
73
|
+
colors=False,
|
|
74
|
+
raw=False,
|
|
75
|
+
capture=True,
|
|
76
|
+
patchers=[],
|
|
77
|
+
extra={},
|
|
78
|
+
)
|
|
79
|
+
log.add(sys.stderr, level=loglevel, format=LOGURU_FORMAT)
|
|
80
|
+
logname = Path(outlogfile).name
|
|
81
|
+
if logname != "-":
|
|
82
|
+
logfile = self.path.joinpath(logname)
|
|
83
|
+
log.add(logfile, level=loglevel, format=LOGURU_FORMAT)
|
|
84
|
+
|
|
85
|
+
# logging directory configuration
|
|
86
|
+
if hydracfg is not None:
|
|
87
|
+
output_dir = hydracfg.runtime.output_dir
|
|
88
|
+
else:
|
|
89
|
+
output_dir = config.outputs
|
|
90
|
+
output_dir = Path(output_dir).absolute()
|
|
91
|
+
self._log_length = int( # the maximum length of the line in loguru
|
|
92
|
+
self.__LOGURU_TOTAL_LENGTH # 80
|
|
93
|
+
- self.__LOGURU_FORMAT_LENGTH
|
|
94
|
+
)
|
|
95
|
+
log.info("=" * self._log_length)
|
|
96
|
+
log.info("The Configuration:\n" + OmegaConf.to_yaml(config))
|
|
97
|
+
log.info(f"Working floder : {os.getcwd()}")
|
|
98
|
+
log.info(f"self.path floder : {self.path}")
|
|
99
|
+
log.info(f"Output floder : {output_dir}")
|
|
100
|
+
log.info(f"Output logfile : {outlogfile}")
|
|
101
|
+
log.info(f"Output loglevel : {loglevel.upper()}")
|
|
102
|
+
log.info("=" * self._log_length)
|
|
103
|
+
|
|
104
|
+
# restart/initialize configuration
|
|
105
|
+
self.network: ReactionNetwork = ReactionNetwork(
|
|
106
|
+
path=self.path / "event",
|
|
107
|
+
config=config.event,
|
|
108
|
+
format=config.event.db_format,
|
|
109
|
+
restart=config.restart,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# check parallel mode
|
|
113
|
+
parallel = str(config.parallel).lower()
|
|
114
|
+
if hydracfg is not None and hydracfg.mode == "MULTIRUN":
|
|
115
|
+
if parallel != "serial":
|
|
116
|
+
msg = (
|
|
117
|
+
"Please delete '--multirun,-m' option "
|
|
118
|
+
"when running this script. The multirun "
|
|
119
|
+
"mode is not supported because this program "
|
|
120
|
+
f"will be parallelized by '{parallel}' innerly."
|
|
121
|
+
)
|
|
122
|
+
self.logger.error(self._reformat_message(msg))
|
|
123
|
+
raise ValueError(msg)
|
|
124
|
+
if parallel not in [
|
|
125
|
+
"serial",
|
|
126
|
+
"multiprocessing",
|
|
127
|
+
"ray",
|
|
128
|
+
"dask",
|
|
129
|
+
"executorlib",
|
|
130
|
+
]:
|
|
131
|
+
msg = (
|
|
132
|
+
f"Invalid parallel mode: {parallel}. Please choose one from "
|
|
133
|
+
"'serial', 'multiprocessing', 'ray', 'dask', 'executorlib'."
|
|
134
|
+
)
|
|
135
|
+
self.logger.error(self._reformat_message(msg))
|
|
136
|
+
raise ValueError(msg)
|
|
137
|
+
pworkers = int(self.config.parallel_workers)
|
|
138
|
+
pworkers: int | None = None if pworkers <= 0 else pworkers
|
|
139
|
+
self.executor = get_executor(parallel, pworkers)
|
|
140
|
+
|
|
141
|
+
self.__gas_lst: list[Gas] = []
|
|
142
|
+
for gas_info in self.network.metadata.basic.gas_info_lst:
|
|
143
|
+
gas, _, cost_time = helper_optimization(
|
|
144
|
+
config=self.config,
|
|
145
|
+
graph=Gas.from_name(
|
|
146
|
+
gas_info.name,
|
|
147
|
+
sticking=gas_info.sticking,
|
|
148
|
+
pressure=gas_info.pressure,
|
|
149
|
+
parse_bonds=self.config.bonds, # type: ignore
|
|
150
|
+
),
|
|
151
|
+
raise_when_fail=True,
|
|
152
|
+
allow_hash_change=False,
|
|
153
|
+
allow_not_connected=False,
|
|
154
|
+
run_vibration=True,
|
|
155
|
+
deep_copy=True,
|
|
156
|
+
)
|
|
157
|
+
if isinstance(gas, Gas):
|
|
158
|
+
self.logger.info(
|
|
159
|
+
self._reformat_message(
|
|
160
|
+
f"Optimized gas({gas_info.name}) "
|
|
161
|
+
f"in {cost_time:.2f} seconds."
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
self.__gas_lst.append(gas)
|
|
165
|
+
self.network.db_gas.add(gas)
|
|
166
|
+
else:
|
|
167
|
+
msg = f"Failed to optimize gas({gas_info.name})."
|
|
168
|
+
self.logger.error(self._reformat_message(msg))
|
|
169
|
+
raise ValueError(msg)
|
|
170
|
+
# persist the network for restart. [gas list]
|
|
171
|
+
self.network.persistence()
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def gas_lst(self) -> list[Gas]:
|
|
175
|
+
if len(self.network.metadata.basic.gas_info_lst) != 0:
|
|
176
|
+
return self.__gas_lst
|
|
177
|
+
else:
|
|
178
|
+
return []
|
|
179
|
+
|
|
180
|
+
@abstractmethod
|
|
181
|
+
def run(self, *args, **kwargs) -> Any:
|
|
182
|
+
"""Run the class."""
|
|
183
|
+
|
|
184
|
+
def get_system_for(self, inp: Atoms | None) -> System:
|
|
185
|
+
if inp is None:
|
|
186
|
+
# parse system for first step
|
|
187
|
+
try:
|
|
188
|
+
result: System = hydra_parse(self.config.system, System)
|
|
189
|
+
except Exception:
|
|
190
|
+
atoms = hydra_parse(self.config.system, Atoms)
|
|
191
|
+
result: System = System.from_ase(
|
|
192
|
+
atoms=atoms,
|
|
193
|
+
parse_bonds=self.config.bonds, # type: ignore
|
|
194
|
+
parse_atoms_is_outer_or_not=True,
|
|
195
|
+
)
|
|
196
|
+
if self.config.system.get("attach_is_adsorbate", True):
|
|
197
|
+
result = result.model_copy(
|
|
198
|
+
update=dict(
|
|
199
|
+
is_adsorbate=np.zeros_like(
|
|
200
|
+
result.is_outer,
|
|
201
|
+
dtype=bool,
|
|
202
|
+
)
|
|
203
|
+
),
|
|
204
|
+
deep=False,
|
|
205
|
+
)
|
|
206
|
+
elif isinstance(inp, Atoms):
|
|
207
|
+
result: System = System.from_ase(
|
|
208
|
+
atoms=inp,
|
|
209
|
+
parse_bonds=self.config.bonds, # type: ignore
|
|
210
|
+
parse_atoms_is_outer_or_not=True,
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
msg = f"Unknown type of input: {type(inp)}"
|
|
214
|
+
self.logger.error(self._reformat_message(msg))
|
|
215
|
+
raise ValueError(msg)
|
|
216
|
+
|
|
217
|
+
assert isinstance(result, System)
|
|
218
|
+
assert result.pair is not None
|
|
219
|
+
assert result.is_outer is not None
|
|
220
|
+
self.logger.info(self._reformat_message(f"Read the system: {result}"))
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def wrap_line(
|
|
225
|
+
text: str,
|
|
226
|
+
width_min: int = 80,
|
|
227
|
+
width_max: int = 100,
|
|
228
|
+
) -> list[str]:
|
|
229
|
+
"""Make a line of text fit in a given width.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
text: The text to wrap.
|
|
233
|
+
width_min: The minimum width of the line.
|
|
234
|
+
width_max: The maximum width of the line.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
The wrapped lines.
|
|
238
|
+
"""
|
|
239
|
+
words = text.split()
|
|
240
|
+
if not words:
|
|
241
|
+
return [""]
|
|
242
|
+
|
|
243
|
+
lines = []
|
|
244
|
+
cur = ""
|
|
245
|
+
for w in words:
|
|
246
|
+
if not cur:
|
|
247
|
+
cur = w
|
|
248
|
+
elif len(cur) + 1 + len(w) <= width_max:
|
|
249
|
+
cur += " " + w
|
|
250
|
+
else:
|
|
251
|
+
lines.append(cur)
|
|
252
|
+
cur = w
|
|
253
|
+
if cur:
|
|
254
|
+
lines.append(cur)
|
|
255
|
+
|
|
256
|
+
if len(lines) >= 2 and len(lines[-1]) < width_min:
|
|
257
|
+
merged = lines[-2] + " " + lines[-1]
|
|
258
|
+
if len(merged) <= width_max:
|
|
259
|
+
lines[-2] = merged
|
|
260
|
+
lines.pop()
|
|
261
|
+
|
|
262
|
+
return lines
|