reaxkit 1.0.0__py3-none-any.whl
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.
- reaxkit/__init__.py +0 -0
- reaxkit/analysis/__init__.py +0 -0
- reaxkit/analysis/composed/RDF_analyzer.py +560 -0
- reaxkit/analysis/composed/__init__.py +0 -0
- reaxkit/analysis/composed/connectivity_analyzer.py +706 -0
- reaxkit/analysis/composed/coordination_analyzer.py +144 -0
- reaxkit/analysis/composed/electrostatics_analyzer.py +687 -0
- reaxkit/analysis/per_file/__init__.py +0 -0
- reaxkit/analysis/per_file/control_analyzer.py +165 -0
- reaxkit/analysis/per_file/eregime_analyzer.py +108 -0
- reaxkit/analysis/per_file/ffield_analyzer.py +305 -0
- reaxkit/analysis/per_file/fort13_analyzer.py +79 -0
- reaxkit/analysis/per_file/fort57_analyzer.py +106 -0
- reaxkit/analysis/per_file/fort73_analyzer.py +61 -0
- reaxkit/analysis/per_file/fort74_analyzer.py +65 -0
- reaxkit/analysis/per_file/fort76_analyzer.py +191 -0
- reaxkit/analysis/per_file/fort78_analyzer.py +154 -0
- reaxkit/analysis/per_file/fort79_analyzer.py +83 -0
- reaxkit/analysis/per_file/fort7_analyzer.py +393 -0
- reaxkit/analysis/per_file/fort99_analyzer.py +411 -0
- reaxkit/analysis/per_file/molfra_analyzer.py +359 -0
- reaxkit/analysis/per_file/params_analyzer.py +258 -0
- reaxkit/analysis/per_file/summary_analyzer.py +84 -0
- reaxkit/analysis/per_file/trainset_analyzer.py +84 -0
- reaxkit/analysis/per_file/vels_analyzer.py +95 -0
- reaxkit/analysis/per_file/xmolout_analyzer.py +528 -0
- reaxkit/cli.py +181 -0
- reaxkit/count_loc.py +276 -0
- reaxkit/data/alias.yaml +89 -0
- reaxkit/data/constants.yaml +27 -0
- reaxkit/data/reaxff_input_files_contents.yaml +186 -0
- reaxkit/data/reaxff_output_files_contents.yaml +301 -0
- reaxkit/data/units.yaml +38 -0
- reaxkit/help/__init__.py +0 -0
- reaxkit/help/help_index_loader.py +531 -0
- reaxkit/help/introspection_utils.py +131 -0
- reaxkit/io/__init__.py +0 -0
- reaxkit/io/base_handler.py +165 -0
- reaxkit/io/generators/__init__.py +0 -0
- reaxkit/io/generators/control_generator.py +123 -0
- reaxkit/io/generators/eregime_generator.py +341 -0
- reaxkit/io/generators/geo_generator.py +967 -0
- reaxkit/io/generators/trainset_generator.py +1758 -0
- reaxkit/io/generators/tregime_generator.py +113 -0
- reaxkit/io/generators/vregime_generator.py +164 -0
- reaxkit/io/generators/xmolout_generator.py +304 -0
- reaxkit/io/handlers/__init__.py +0 -0
- reaxkit/io/handlers/control_handler.py +209 -0
- reaxkit/io/handlers/eregime_handler.py +122 -0
- reaxkit/io/handlers/ffield_handler.py +812 -0
- reaxkit/io/handlers/fort13_handler.py +123 -0
- reaxkit/io/handlers/fort57_handler.py +143 -0
- reaxkit/io/handlers/fort73_handler.py +145 -0
- reaxkit/io/handlers/fort74_handler.py +155 -0
- reaxkit/io/handlers/fort76_handler.py +195 -0
- reaxkit/io/handlers/fort78_handler.py +142 -0
- reaxkit/io/handlers/fort79_handler.py +227 -0
- reaxkit/io/handlers/fort7_handler.py +264 -0
- reaxkit/io/handlers/fort99_handler.py +128 -0
- reaxkit/io/handlers/geo_handler.py +224 -0
- reaxkit/io/handlers/molfra_handler.py +184 -0
- reaxkit/io/handlers/params_handler.py +137 -0
- reaxkit/io/handlers/summary_handler.py +135 -0
- reaxkit/io/handlers/trainset_handler.py +658 -0
- reaxkit/io/handlers/vels_handler.py +293 -0
- reaxkit/io/handlers/xmolout_handler.py +174 -0
- reaxkit/utils/__init__.py +0 -0
- reaxkit/utils/alias.py +219 -0
- reaxkit/utils/cache.py +77 -0
- reaxkit/utils/constants.py +75 -0
- reaxkit/utils/equation_of_states.py +96 -0
- reaxkit/utils/exceptions.py +27 -0
- reaxkit/utils/frame_utils.py +175 -0
- reaxkit/utils/log.py +43 -0
- reaxkit/utils/media/__init__.py +0 -0
- reaxkit/utils/media/convert.py +90 -0
- reaxkit/utils/media/make_video.py +91 -0
- reaxkit/utils/media/plotter.py +812 -0
- reaxkit/utils/numerical/__init__.py +0 -0
- reaxkit/utils/numerical/extrema_finder.py +96 -0
- reaxkit/utils/numerical/moving_average.py +103 -0
- reaxkit/utils/numerical/numerical_calcs.py +75 -0
- reaxkit/utils/numerical/signal_ops.py +135 -0
- reaxkit/utils/path.py +55 -0
- reaxkit/utils/units.py +104 -0
- reaxkit/webui/__init__.py +0 -0
- reaxkit/webui/app.py +0 -0
- reaxkit/webui/components.py +0 -0
- reaxkit/webui/layouts.py +0 -0
- reaxkit/webui/utils.py +0 -0
- reaxkit/workflows/__init__.py +0 -0
- reaxkit/workflows/composed/__init__.py +0 -0
- reaxkit/workflows/composed/coordination_workflow.py +393 -0
- reaxkit/workflows/composed/electrostatics_workflow.py +587 -0
- reaxkit/workflows/composed/xmolout_fort7_workflow.py +343 -0
- reaxkit/workflows/meta/__init__.py +0 -0
- reaxkit/workflows/meta/help_workflow.py +136 -0
- reaxkit/workflows/meta/introspection_workflow.py +235 -0
- reaxkit/workflows/meta/make_video_workflow.py +61 -0
- reaxkit/workflows/meta/plotter_workflow.py +601 -0
- reaxkit/workflows/per_file/__init__.py +0 -0
- reaxkit/workflows/per_file/control_workflow.py +110 -0
- reaxkit/workflows/per_file/eregime_workflow.py +267 -0
- reaxkit/workflows/per_file/ffield_workflow.py +390 -0
- reaxkit/workflows/per_file/fort13_workflow.py +86 -0
- reaxkit/workflows/per_file/fort57_workflow.py +137 -0
- reaxkit/workflows/per_file/fort73_workflow.py +151 -0
- reaxkit/workflows/per_file/fort74_workflow.py +88 -0
- reaxkit/workflows/per_file/fort76_workflow.py +188 -0
- reaxkit/workflows/per_file/fort78_workflow.py +135 -0
- reaxkit/workflows/per_file/fort79_workflow.py +314 -0
- reaxkit/workflows/per_file/fort7_workflow.py +592 -0
- reaxkit/workflows/per_file/fort83_workflow.py +60 -0
- reaxkit/workflows/per_file/fort99_workflow.py +223 -0
- reaxkit/workflows/per_file/geo_workflow.py +554 -0
- reaxkit/workflows/per_file/molfra_workflow.py +577 -0
- reaxkit/workflows/per_file/params_workflow.py +135 -0
- reaxkit/workflows/per_file/summary_workflow.py +161 -0
- reaxkit/workflows/per_file/trainset_workflow.py +356 -0
- reaxkit/workflows/per_file/tregime_workflow.py +79 -0
- reaxkit/workflows/per_file/vels_workflow.py +309 -0
- reaxkit/workflows/per_file/vregime_workflow.py +75 -0
- reaxkit/workflows/per_file/xmolout_workflow.py +678 -0
- reaxkit-1.0.0.dist-info/METADATA +128 -0
- reaxkit-1.0.0.dist-info/RECORD +130 -0
- reaxkit-1.0.0.dist-info/WHEEL +5 -0
- reaxkit-1.0.0.dist-info/entry_points.txt +2 -0
- reaxkit-1.0.0.dist-info/licenses/AUTHORS.md +20 -0
- reaxkit-1.0.0.dist-info/licenses/LICENSE +21 -0
- reaxkit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Control file analysis utilities.
|
|
3
|
+
|
|
4
|
+
This module provides helper functions for querying and retrieving
|
|
5
|
+
parameters from a parsed ReaxFF control file via `ControlHandler`.
|
|
6
|
+
|
|
7
|
+
Typical use cases include:
|
|
8
|
+
|
|
9
|
+
- discovering available parameter sections
|
|
10
|
+
- listing available control keys
|
|
11
|
+
- retrieving parameter values with safe defaults
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from reaxkit.io.handlers.control_handler import ControlHandler
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_available_sections(handler: ControlHandler) -> list[str]:
|
|
22
|
+
"""List the available parameter sections in a ReaxFF control file.
|
|
23
|
+
|
|
24
|
+
Works on
|
|
25
|
+
--------
|
|
26
|
+
ControlHandler (control)
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
handler : ControlHandler
|
|
31
|
+
Parsed control file handler.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
list[str]
|
|
36
|
+
Section names present in the file (e.g., "general", "md", "mm", "ff", "outdated").
|
|
37
|
+
|
|
38
|
+
Examples
|
|
39
|
+
--------
|
|
40
|
+
>>> from reaxkit.io.handlers.control_handler import ControlHandler
|
|
41
|
+
>>> from reaxkit.analysis.per_file.control_analyzer import _get_available_sections
|
|
42
|
+
>>> h = ControlHandler("control")
|
|
43
|
+
>>> sections = _get_available_sections(h)
|
|
44
|
+
"""
|
|
45
|
+
sections = []
|
|
46
|
+
if handler.general_parameters:
|
|
47
|
+
sections.append("general")
|
|
48
|
+
if handler.md_parameters:
|
|
49
|
+
sections.append("md")
|
|
50
|
+
if handler.mm_parameters:
|
|
51
|
+
sections.append("mm")
|
|
52
|
+
if handler.ff_parameters:
|
|
53
|
+
sections.append("ff")
|
|
54
|
+
if handler.outdated_parameters:
|
|
55
|
+
sections.append("outdated")
|
|
56
|
+
return sections
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _get_available_keys(handler: ControlHandler, section: Optional[str] = None) -> list[str]:
|
|
60
|
+
"""List available control-file parameter keys, optionally within a specific section.
|
|
61
|
+
|
|
62
|
+
Works on
|
|
63
|
+
--------
|
|
64
|
+
ControlHandler (control)
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
handler : ControlHandler
|
|
69
|
+
Parsed control file handler.
|
|
70
|
+
section : str, optional
|
|
71
|
+
Section to query (one of: "general", "md", "mm", "ff", "outdated").
|
|
72
|
+
If None, keys from all sections are combined.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
list[str]
|
|
77
|
+
Sorted list of parameter keys.
|
|
78
|
+
|
|
79
|
+
Examples
|
|
80
|
+
--------
|
|
81
|
+
>>> from reaxkit.io.handlers.control_handler import ControlHandler
|
|
82
|
+
>>> from reaxkit.analysis.per_file.control_analyzer import _get_available_keys
|
|
83
|
+
>>> h = ControlHandler("control")
|
|
84
|
+
>>> keys = _get_available_keys(h, section="md")
|
|
85
|
+
"""
|
|
86
|
+
section_map = {
|
|
87
|
+
"general": handler.general_parameters,
|
|
88
|
+
"md": handler.md_parameters,
|
|
89
|
+
"mm": handler.mm_parameters,
|
|
90
|
+
"ff": handler.ff_parameters,
|
|
91
|
+
"outdated": handler.outdated_parameters,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if section:
|
|
95
|
+
section = section.lower()
|
|
96
|
+
if section not in section_map:
|
|
97
|
+
raise ValueError(f"❌ Unknown section: {section}")
|
|
98
|
+
return sorted(section_map[section].keys())
|
|
99
|
+
|
|
100
|
+
# all keys from all sections
|
|
101
|
+
all_keys = set()
|
|
102
|
+
for d in section_map.values():
|
|
103
|
+
all_keys.update(d.keys())
|
|
104
|
+
return sorted(all_keys)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_control_data(
|
|
108
|
+
handler: ControlHandler,
|
|
109
|
+
key: str,
|
|
110
|
+
section: Optional[str] = None,
|
|
111
|
+
default=None,
|
|
112
|
+
):
|
|
113
|
+
"""
|
|
114
|
+
Retrieve a control-file parameter value by key, optionally restricted to a section.
|
|
115
|
+
|
|
116
|
+
Works on
|
|
117
|
+
--------
|
|
118
|
+
ControlHandler (control)
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
handler : ControlHandler
|
|
123
|
+
Parsed control file handler.
|
|
124
|
+
key : str
|
|
125
|
+
Parameter name (case-insensitive).
|
|
126
|
+
section : str, optional
|
|
127
|
+
Section to search (one of: "general", "md", "mm", "ff", "outdated").
|
|
128
|
+
If None, all sections are searched in order.
|
|
129
|
+
default : any, optional
|
|
130
|
+
Value to return if the key is not found.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
any
|
|
135
|
+
The parameter value if found; otherwise `default`.
|
|
136
|
+
|
|
137
|
+
Examples
|
|
138
|
+
--------
|
|
139
|
+
>>> from reaxkit.io.handlers.control_handler import ControlHandler
|
|
140
|
+
>>> from reaxkit.analysis.per_file.control_analyzer import get_control_data
|
|
141
|
+
>>> h = ControlHandler("control")
|
|
142
|
+
>>> tstep = get_control_data(h, "tstep", section="md", default=None)
|
|
143
|
+
"""
|
|
144
|
+
key = key.lower()
|
|
145
|
+
section_map = {
|
|
146
|
+
"general": handler.general_parameters,
|
|
147
|
+
"md": handler.md_parameters,
|
|
148
|
+
"mm": handler.mm_parameters,
|
|
149
|
+
"ff": handler.ff_parameters,
|
|
150
|
+
"outdated": handler.outdated_parameters,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if section:
|
|
154
|
+
section = section.lower()
|
|
155
|
+
if section not in section_map:
|
|
156
|
+
raise ValueError(f"❌ Unknown section: {section}")
|
|
157
|
+
try:
|
|
158
|
+
return section_map[section][key]
|
|
159
|
+
except:
|
|
160
|
+
return default
|
|
161
|
+
|
|
162
|
+
for d in section_map.values():
|
|
163
|
+
if key in d:
|
|
164
|
+
return d[key]
|
|
165
|
+
return default
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Eregime (eregime.in) analysis utilities.
|
|
2
|
+
|
|
3
|
+
This module provides helpers for extracting and plotting electric-field regime
|
|
4
|
+
data from a parsed ``eregime.in`` file via ``EregimeHandler``.
|
|
5
|
+
|
|
6
|
+
Typical use cases include:
|
|
7
|
+
|
|
8
|
+
- selecting a column (with alias support) such as field magnitude or direction
|
|
9
|
+
- converting the x-axis from iteration to frame index or physical time
|
|
10
|
+
- exporting a clean two-column table for plotting
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
import pandas as pd
|
|
16
|
+
|
|
17
|
+
from reaxkit.io.handlers.eregime_handler import EregimeHandler
|
|
18
|
+
from reaxkit.utils.alias import resolve_alias_from_columns, normalize_choice # uses shared alias map
|
|
19
|
+
from reaxkit.utils.media.convert import convert_xaxis # converts iter → time or frame
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _resolve_with_fallback(df: pd.DataFrame, name: str) -> str:
|
|
23
|
+
"""
|
|
24
|
+
Resolve a requested column name using the shared alias map.
|
|
25
|
+
Falls back to enumerated columns (field1/field_dir1, etc.) if generic keys are absent.
|
|
26
|
+
"""
|
|
27
|
+
if not name:
|
|
28
|
+
raise ValueError("Column name is empty.")
|
|
29
|
+
|
|
30
|
+
# Normalize common variants (case-insensitive) to a canonical key
|
|
31
|
+
canonical = normalize_choice(name)
|
|
32
|
+
|
|
33
|
+
# Try direct/aliased match
|
|
34
|
+
hit = resolve_alias_from_columns(df.columns, canonical)
|
|
35
|
+
if hit:
|
|
36
|
+
return hit
|
|
37
|
+
|
|
38
|
+
# Fallbacks for generic single-zone keys in multi-zone files
|
|
39
|
+
if canonical in ("field", "field_dir"):
|
|
40
|
+
# Prefer the first enumerated pair if present
|
|
41
|
+
for i in range(1, 16): # generous upper bound
|
|
42
|
+
cand = f"{canonical}{i}" if canonical != "field_dir" else f"field_dir{i}"
|
|
43
|
+
hit = resolve_alias_from_columns(df.columns, cand)
|
|
44
|
+
if hit:
|
|
45
|
+
return hit
|
|
46
|
+
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"Column '{name}' not found (after alias resolution). "
|
|
49
|
+
f"Available columns: {list(df.columns)}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_eregime_data(
|
|
54
|
+
handler: EregimeHandler,
|
|
55
|
+
y: str,
|
|
56
|
+
xaxis: str = "iter",
|
|
57
|
+
control_file: str = "control",
|
|
58
|
+
) -> pd.DataFrame:
|
|
59
|
+
"""Extract a two-column table of a selected ``eregime.in`` quantity versus an x-axis.
|
|
60
|
+
|
|
61
|
+
Works on
|
|
62
|
+
--------
|
|
63
|
+
EregimeHandler — ``eregime.in``
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
handler : EregimeHandler
|
|
68
|
+
Parsed ``eregime.in`` handler.
|
|
69
|
+
y : str
|
|
70
|
+
Name of the y-column to extract. Aliases are supported (e.g., ``E``, ``Ef``,
|
|
71
|
+
``Magnitude(V/A)`` → field; ``direction``/``dir`` → field direction).
|
|
72
|
+
For multi-zone files, you may request enumerated keys like ``field2`` or
|
|
73
|
+
``field_dir3``.
|
|
74
|
+
xaxis : {"iter", "frame", "time"}, default="iter"
|
|
75
|
+
X-axis to use:
|
|
76
|
+
- ``iter``: raw iteration index from the file
|
|
77
|
+
- ``frame``: 0..N-1
|
|
78
|
+
- ``time``: converts iteration → time using ``control_file`` and auto-scales fs/ps/ns
|
|
79
|
+
control_file : str, default="control"
|
|
80
|
+
Path to the ReaxFF control file used for ``xaxis="time"`` conversion.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
pandas.DataFrame
|
|
85
|
+
Two-column table with columns: ``[x_label, y]`` where ``x_label`` is one of
|
|
86
|
+
``iter``, ``Frame``, or ``Time (fs/ps/ns)``.
|
|
87
|
+
|
|
88
|
+
Examples
|
|
89
|
+
--------
|
|
90
|
+
>>> from reaxkit.io.handlers.eregime_handler import EregimeHandler
|
|
91
|
+
>>> from reaxkit.analysis.per_file.eregime_analyzer import get_eregime_data
|
|
92
|
+
>>> h = EregimeHandler("eregime.in")
|
|
93
|
+
>>> df = get_eregime_data(h, y="E", xaxis="time", control_file="control")
|
|
94
|
+
"""
|
|
95
|
+
df = handler.dataframe()
|
|
96
|
+
|
|
97
|
+
# Resolve iteration column (needed for x conversion)
|
|
98
|
+
iter_col = _resolve_with_fallback(df, "iter")
|
|
99
|
+
|
|
100
|
+
# Resolve Y column with aliases + fallbacks
|
|
101
|
+
y_col = _resolve_with_fallback(df, y)
|
|
102
|
+
|
|
103
|
+
# Convert x-axis
|
|
104
|
+
x_vals, x_label = convert_xaxis(df[iter_col].to_numpy(), xaxis=xaxis, control_file=control_file)
|
|
105
|
+
|
|
106
|
+
out = pd.DataFrame({x_label: x_vals, y: df[y_col].to_numpy()})
|
|
107
|
+
out = out.sort_values(x_label).reset_index(drop=True)
|
|
108
|
+
return out
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ReaxFF force-field (ffield) analysis utilities.
|
|
3
|
+
|
|
4
|
+
This module provides helper functions for extracting and interpreting
|
|
5
|
+
sections of a ReaxFF ``ffield`` file via ``FFieldHandler``.
|
|
6
|
+
|
|
7
|
+
Typical use cases include:
|
|
8
|
+
|
|
9
|
+
- retrieving raw parameter tables for specific ffield sections
|
|
10
|
+
- interpreting numeric atom indices (e.g. 1–1) into chemical symbols (e.g. C–C)
|
|
11
|
+
- generating human-readable interaction labels for bonds, angles, torsions, and H-bonds
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
from typing import Dict, Iterable, Optional, Sequence
|
|
17
|
+
|
|
18
|
+
import pandas as pd
|
|
19
|
+
|
|
20
|
+
from reaxkit.io.base_handler import BaseHandler
|
|
21
|
+
from reaxkit.io.handlers.ffield_handler import FFieldHandler
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Map user-friendly names to canonical section keys
|
|
25
|
+
_SECTION_ALIASES: Dict[str, str] = {
|
|
26
|
+
"general": FFieldHandler.SECTION_GENERAL,
|
|
27
|
+
|
|
28
|
+
"atom": FFieldHandler.SECTION_ATOM,
|
|
29
|
+
"atoms": FFieldHandler.SECTION_ATOM,
|
|
30
|
+
|
|
31
|
+
"bond": FFieldHandler.SECTION_BOND,
|
|
32
|
+
"bonds": FFieldHandler.SECTION_BOND,
|
|
33
|
+
|
|
34
|
+
"off_diagonal": FFieldHandler.SECTION_OFF_DIAGONAL,
|
|
35
|
+
"off-diagonal": FFieldHandler.SECTION_OFF_DIAGONAL,
|
|
36
|
+
"offdiag": FFieldHandler.SECTION_OFF_DIAGONAL,
|
|
37
|
+
|
|
38
|
+
"angle": FFieldHandler.SECTION_ANGLE,
|
|
39
|
+
"angles": FFieldHandler.SECTION_ANGLE,
|
|
40
|
+
|
|
41
|
+
"torsion": FFieldHandler.SECTION_TORSION,
|
|
42
|
+
"torsions": FFieldHandler.SECTION_TORSION,
|
|
43
|
+
|
|
44
|
+
"hbond": FFieldHandler.SECTION_HBOND,
|
|
45
|
+
"hbonds": FFieldHandler.SECTION_HBOND,
|
|
46
|
+
"hydrogen_bond": FFieldHandler.SECTION_HBOND,
|
|
47
|
+
"hydrogen_bonds": FFieldHandler.SECTION_HBOND,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _normalize_section_name(section: str) -> str:
|
|
52
|
+
"""Normalize user input like 'Off-Diagonal', ' off diag ' -> 'off_diagonal'."""
|
|
53
|
+
return section.strip().lower().replace("-", "_").replace(" ", "_")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_ffield_data(handler: BaseHandler, *, section: str) -> pd.DataFrame:
|
|
57
|
+
"""
|
|
58
|
+
Retrieve a specific section of the ReaxFF force-field file as a DataFrame.
|
|
59
|
+
|
|
60
|
+
Works on
|
|
61
|
+
--------
|
|
62
|
+
FFieldHandler — ``ffield``
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
handler : FFieldHandler
|
|
67
|
+
Parsed force-field handler.
|
|
68
|
+
section : str
|
|
69
|
+
Section name to retrieve (e.g. ``atom``, ``bond``, ``off_diagonal``,
|
|
70
|
+
``angle``, ``torsion``, ``hbond``). Aliases are supported.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
pandas.DataFrame
|
|
75
|
+
Table of parameters for the requested section.
|
|
76
|
+
|
|
77
|
+
Examples
|
|
78
|
+
--------
|
|
79
|
+
>>> from reaxkit.io.handlers.ffield_handler import FFieldHandler
|
|
80
|
+
>>> from reaxkit.analysis.per_file.ffield_analyzer import get_ffield_data
|
|
81
|
+
>>> h = FFieldHandler("ffield")
|
|
82
|
+
>>> df = get_ffield_data(h, section="bond")
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
# Type check
|
|
86
|
+
if not isinstance(handler, FFieldHandler):
|
|
87
|
+
raise TypeError(
|
|
88
|
+
f"get_sections_data requires an FFieldHandler, got {type(handler)!r}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Normalize the name
|
|
92
|
+
norm = _normalize_section_name(section)
|
|
93
|
+
|
|
94
|
+
if norm not in _SECTION_ALIASES:
|
|
95
|
+
raise KeyError(
|
|
96
|
+
f"Unknown section {section!r}. Valid options: {sorted(_SECTION_ALIASES.keys())}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Resolve canonical key
|
|
100
|
+
canonical = _SECTION_ALIASES[norm]
|
|
101
|
+
|
|
102
|
+
# Use your kept section_df method
|
|
103
|
+
try:
|
|
104
|
+
df = handler.section_df(canonical)
|
|
105
|
+
except KeyError:
|
|
106
|
+
raise KeyError(
|
|
107
|
+
f"Section {section!r} (canonical key {canonical!r}) not found in this ffield."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Always return a copy
|
|
111
|
+
return df.copy()
|
|
112
|
+
|
|
113
|
+
###############################################################################
|
|
114
|
+
# A “interpret indices → atom symbols” utility.
|
|
115
|
+
# It uses the atom section to build an index -> symbol map for bond,
|
|
116
|
+
# off-diagonal, angle, torsion, hbond.
|
|
117
|
+
# This is used to understand what 1 1 mean in bond data, which shows C-C
|
|
118
|
+
# bond if C is the atom number 1 in the ffield.
|
|
119
|
+
###############################################################################
|
|
120
|
+
|
|
121
|
+
def _atom_index_to_symbol_map(handler: FFieldHandler) -> Dict[int, str]:
|
|
122
|
+
"""
|
|
123
|
+
Build {atom_index: symbol} from the atom section.
|
|
124
|
+
|
|
125
|
+
Notes:
|
|
126
|
+
- atom_df index is 'atom_index' (1-based in your parser).
|
|
127
|
+
- symbol column is expected to be like 'C', 'H', 'O', ...
|
|
128
|
+
"""
|
|
129
|
+
atom_df = handler.section_df(FFieldHandler.SECTION_ATOM)
|
|
130
|
+
|
|
131
|
+
if "symbol" not in atom_df.columns:
|
|
132
|
+
raise KeyError(
|
|
133
|
+
"Atom section does not contain a 'symbol' column. "
|
|
134
|
+
"Check FFieldHandler._parse_atom_section()."
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
out: Dict[int, str] = {}
|
|
138
|
+
for idx, row in atom_df.iterrows():
|
|
139
|
+
try:
|
|
140
|
+
i = int(idx)
|
|
141
|
+
except Exception:
|
|
142
|
+
continue
|
|
143
|
+
sym = row.get("symbol")
|
|
144
|
+
if sym is None or (isinstance(sym, float) and pd.isna(sym)) or str(sym).strip() == "":
|
|
145
|
+
sym = f"atom{i}"
|
|
146
|
+
out[i] = str(sym).strip()
|
|
147
|
+
return out
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _add_symbols_for_columns(
|
|
151
|
+
df: pd.DataFrame,
|
|
152
|
+
idx_to_sym: Dict[int, str],
|
|
153
|
+
cols: Sequence[str],
|
|
154
|
+
*,
|
|
155
|
+
term_col: str = "term",
|
|
156
|
+
sep: str = "-",
|
|
157
|
+
) -> pd.DataFrame:
|
|
158
|
+
"""
|
|
159
|
+
For each integer index column in `cols` (e.g. ['i','j','k']),
|
|
160
|
+
add '<col>_symbol' columns and a combined `term` column.
|
|
161
|
+
"""
|
|
162
|
+
out = df.copy()
|
|
163
|
+
|
|
164
|
+
sym_cols: list[str] = []
|
|
165
|
+
for c in cols:
|
|
166
|
+
sc = f"{c}_symbol"
|
|
167
|
+
sym_cols.append(sc)
|
|
168
|
+
|
|
169
|
+
def _map_one(v):
|
|
170
|
+
if v is None or (isinstance(v, float) and pd.isna(v)):
|
|
171
|
+
return None
|
|
172
|
+
try:
|
|
173
|
+
iv = int(v)
|
|
174
|
+
except Exception:
|
|
175
|
+
return None
|
|
176
|
+
return idx_to_sym.get(iv, f"atom{iv}")
|
|
177
|
+
|
|
178
|
+
out[sc] = out[c].map(_map_one)
|
|
179
|
+
|
|
180
|
+
# Build readable label: e.g. C-H, C-C-C, O-H-O
|
|
181
|
+
out[term_col] = out[sym_cols].apply(
|
|
182
|
+
lambda r: sep.join([x for x in r.tolist() if x is not None]),
|
|
183
|
+
axis=1,
|
|
184
|
+
)
|
|
185
|
+
return out
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def interpret_ffield_terms(
|
|
189
|
+
handler: FFieldHandler,
|
|
190
|
+
*,
|
|
191
|
+
sections: Optional[Iterable[str]] = None,
|
|
192
|
+
sep: str = "-",
|
|
193
|
+
) -> Dict[str, pd.DataFrame]:
|
|
194
|
+
"""
|
|
195
|
+
Interpret numeric atom indices in ffield sections into chemical symbols.
|
|
196
|
+
|
|
197
|
+
Works on
|
|
198
|
+
--------
|
|
199
|
+
FFieldHandler — ``ffield``
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
handler : FFieldHandler
|
|
204
|
+
Parsed force-field handler.
|
|
205
|
+
sections : iterable of str, optional
|
|
206
|
+
Sections to interpret. Default interprets all relevant sections:
|
|
207
|
+
``bond``, ``off_diagonal``, ``angle``, ``torsion``, ``hbond``.
|
|
208
|
+
sep : str, default="-"
|
|
209
|
+
Separator used when building human-readable interaction labels.
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
dict[str, pandas.DataFrame]
|
|
214
|
+
Mapping from section name to interpreted DataFrame. Each DataFrame
|
|
215
|
+
includes:
|
|
216
|
+
- ``*_symbol`` columns (e.g. ``i_symbol``, ``j_symbol``)
|
|
217
|
+
- ``term`` column with readable labels (e.g. ``C-H``, ``C-C-C``)
|
|
218
|
+
|
|
219
|
+
Examples
|
|
220
|
+
--------
|
|
221
|
+
>>> from reaxkit.io.handlers.ffield_handler import FFieldHandler
|
|
222
|
+
>>> from reaxkit.analysis.per_file.ffield_analyzer import interpret_ffield_terms
|
|
223
|
+
>>> h = FFieldHandler("ffield")
|
|
224
|
+
>>> data = interpret_ffield_terms(h, sections=["bond", "angle"])
|
|
225
|
+
>>> bond_df = data["bond"]
|
|
226
|
+
"""
|
|
227
|
+
if not isinstance(handler, FFieldHandler):
|
|
228
|
+
raise TypeError(f"interpret_ffield_terms requires FFieldHandler, got {type(handler)!r}")
|
|
229
|
+
|
|
230
|
+
idx_to_sym = _atom_index_to_symbol_map(handler)
|
|
231
|
+
|
|
232
|
+
targets = [
|
|
233
|
+
FFieldHandler.SECTION_BOND,
|
|
234
|
+
FFieldHandler.SECTION_OFF_DIAGONAL,
|
|
235
|
+
FFieldHandler.SECTION_ANGLE,
|
|
236
|
+
FFieldHandler.SECTION_TORSION,
|
|
237
|
+
FFieldHandler.SECTION_HBOND,
|
|
238
|
+
]
|
|
239
|
+
if sections is not None:
|
|
240
|
+
wanted = {s.strip().lower() for s in sections}
|
|
241
|
+
targets = [t for t in targets if t in wanted]
|
|
242
|
+
|
|
243
|
+
out: Dict[str, pd.DataFrame] = {}
|
|
244
|
+
|
|
245
|
+
for sec in targets:
|
|
246
|
+
df = handler.section_df(sec).copy()
|
|
247
|
+
|
|
248
|
+
if sec in (FFieldHandler.SECTION_BOND, FFieldHandler.SECTION_OFF_DIAGONAL):
|
|
249
|
+
# columns: i, j
|
|
250
|
+
out[sec] = _add_symbols_for_columns(df, idx_to_sym, ["i", "j"], sep=sep)
|
|
251
|
+
elif sec == FFieldHandler.SECTION_ANGLE:
|
|
252
|
+
# columns: i, j, k
|
|
253
|
+
out[sec] = _add_symbols_for_columns(df, idx_to_sym, ["i", "j", "k"], sep=sep)
|
|
254
|
+
elif sec == FFieldHandler.SECTION_TORSION:
|
|
255
|
+
# columns: i, j, k, l
|
|
256
|
+
out[sec] = _add_symbols_for_columns(df, idx_to_sym, ["i", "j", "k", "l"], sep=sep)
|
|
257
|
+
elif sec == FFieldHandler.SECTION_HBOND:
|
|
258
|
+
# columns: i, j, k (commonly X-H-Y order in your example)
|
|
259
|
+
out[sec] = _add_symbols_for_columns(df, idx_to_sym, ["i", "j", "k"], sep=sep)
|
|
260
|
+
else:
|
|
261
|
+
out[sec] = df
|
|
262
|
+
|
|
263
|
+
return out
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def interpret_one_section(
|
|
267
|
+
handler: FFieldHandler,
|
|
268
|
+
*,
|
|
269
|
+
section: str,
|
|
270
|
+
sep: str = "-",
|
|
271
|
+
) -> pd.DataFrame:
|
|
272
|
+
"""
|
|
273
|
+
Interpret a single ffield section into symbol-based interaction labels.
|
|
274
|
+
|
|
275
|
+
Works on
|
|
276
|
+
--------
|
|
277
|
+
FFieldHandler — ``ffield``
|
|
278
|
+
|
|
279
|
+
Parameters
|
|
280
|
+
----------
|
|
281
|
+
handler : FFieldHandler
|
|
282
|
+
Parsed force-field handler.
|
|
283
|
+
section : str
|
|
284
|
+
Section to interpret (e.g. ``bond``, ``angle``, ``torsion``, ``hbond``).
|
|
285
|
+
sep : str, default="-"
|
|
286
|
+
Separator used when building the ``term`` label.
|
|
287
|
+
|
|
288
|
+
Returns
|
|
289
|
+
-------
|
|
290
|
+
pandas.DataFrame
|
|
291
|
+
Interpreted section DataFrame with ``*_symbol`` columns and a
|
|
292
|
+
human-readable ``term`` column.
|
|
293
|
+
|
|
294
|
+
Examples
|
|
295
|
+
--------
|
|
296
|
+
>>> from reaxkit.io.handlers.ffield_handler import FFieldHandler
|
|
297
|
+
>>> from reaxkit.analysis.per_file.ffield_analyzer import interpret_one_section
|
|
298
|
+
>>> h = FFieldHandler("ffield")
|
|
299
|
+
>>> bond_df = interpret_one_section(h, section="bond")
|
|
300
|
+
"""
|
|
301
|
+
section = section.strip().lower().replace("-", "_").replace(" ", "_")
|
|
302
|
+
res = interpret_ffield_terms(handler, sections=[section], sep=sep)
|
|
303
|
+
if section not in res:
|
|
304
|
+
raise KeyError(f"Section {section!r} not found or not supported for interpretation.")
|
|
305
|
+
return res[section]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
fort.13 (training error) analysis utilities.
|
|
3
|
+
|
|
4
|
+
This module provides helper functions for extracting force-field training
|
|
5
|
+
error values from a parsed ReaxFF ``fort.13`` file via ``Fort13Handler``.
|
|
6
|
+
|
|
7
|
+
Typical use cases include:
|
|
8
|
+
|
|
9
|
+
- retrieving total force-field error versus epoch
|
|
10
|
+
- extracting the error series for quick plotting or post-processing
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
import pandas as pd
|
|
17
|
+
|
|
18
|
+
from reaxkit.io.handlers.fort13_handler import Fort13Handler
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_fort13_data(handler: Fort13Handler, epochs: Optional[List[int]] = None) -> pd.DataFrame:
|
|
22
|
+
"""Extract total force-field error for all or selected epochs from ``fort.13``.
|
|
23
|
+
|
|
24
|
+
Works on
|
|
25
|
+
--------
|
|
26
|
+
Fort13Handler — ``fort.13``
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
handler : Fort13Handler
|
|
31
|
+
Parsed ``fort.13`` handler.
|
|
32
|
+
epochs : list[int], optional
|
|
33
|
+
Epoch numbers to include. If None, all epochs are returned.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
pandas.DataFrame
|
|
38
|
+
Error table with columns: ``epoch`` and ``total_ff_error``.
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
>>> from reaxkit.io.handlers.fort13_handler import Fort13Handler
|
|
43
|
+
>>> from reaxkit.analysis.per_file.fort13_analyzer import get_fort13_data
|
|
44
|
+
>>> h = Fort13Handler("fort.13")
|
|
45
|
+
>>> df = get_fort13_data(h, epochs=[1, 10, 50])
|
|
46
|
+
"""
|
|
47
|
+
df = handler.dataframe()
|
|
48
|
+
|
|
49
|
+
if epochs is not None:
|
|
50
|
+
df = df[df["epoch"].isin(epochs)].reset_index(drop=True)
|
|
51
|
+
|
|
52
|
+
return df[["epoch", "total_ff_error"]].copy()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _error_series_across_epochs(handler: Fort13Handler) -> List[float]:
|
|
56
|
+
"""Return the total force-field error values across all epochs as a list.
|
|
57
|
+
|
|
58
|
+
Works on
|
|
59
|
+
--------
|
|
60
|
+
Fort13Handler — ``fort.13``
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
handler : Fort13Handler
|
|
65
|
+
Parsed ``fort.13`` handler.
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
list[float]
|
|
70
|
+
Total force-field error values in epoch order.
|
|
71
|
+
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
>>> from reaxkit.io.handlers.fort13_handler import Fort13Handler
|
|
75
|
+
>>> from reaxkit.analysis.per_file.fort13_analyzer import _error_series_across_epochs
|
|
76
|
+
>>> h = Fort13Handler("fort.13")
|
|
77
|
+
>>> errs = _error_series_across_epochs(h)
|
|
78
|
+
"""
|
|
79
|
+
return handler.dataframe()["total_ff_error"].tolist()
|