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.
Files changed (130) hide show
  1. reaxkit/__init__.py +0 -0
  2. reaxkit/analysis/__init__.py +0 -0
  3. reaxkit/analysis/composed/RDF_analyzer.py +560 -0
  4. reaxkit/analysis/composed/__init__.py +0 -0
  5. reaxkit/analysis/composed/connectivity_analyzer.py +706 -0
  6. reaxkit/analysis/composed/coordination_analyzer.py +144 -0
  7. reaxkit/analysis/composed/electrostatics_analyzer.py +687 -0
  8. reaxkit/analysis/per_file/__init__.py +0 -0
  9. reaxkit/analysis/per_file/control_analyzer.py +165 -0
  10. reaxkit/analysis/per_file/eregime_analyzer.py +108 -0
  11. reaxkit/analysis/per_file/ffield_analyzer.py +305 -0
  12. reaxkit/analysis/per_file/fort13_analyzer.py +79 -0
  13. reaxkit/analysis/per_file/fort57_analyzer.py +106 -0
  14. reaxkit/analysis/per_file/fort73_analyzer.py +61 -0
  15. reaxkit/analysis/per_file/fort74_analyzer.py +65 -0
  16. reaxkit/analysis/per_file/fort76_analyzer.py +191 -0
  17. reaxkit/analysis/per_file/fort78_analyzer.py +154 -0
  18. reaxkit/analysis/per_file/fort79_analyzer.py +83 -0
  19. reaxkit/analysis/per_file/fort7_analyzer.py +393 -0
  20. reaxkit/analysis/per_file/fort99_analyzer.py +411 -0
  21. reaxkit/analysis/per_file/molfra_analyzer.py +359 -0
  22. reaxkit/analysis/per_file/params_analyzer.py +258 -0
  23. reaxkit/analysis/per_file/summary_analyzer.py +84 -0
  24. reaxkit/analysis/per_file/trainset_analyzer.py +84 -0
  25. reaxkit/analysis/per_file/vels_analyzer.py +95 -0
  26. reaxkit/analysis/per_file/xmolout_analyzer.py +528 -0
  27. reaxkit/cli.py +181 -0
  28. reaxkit/count_loc.py +276 -0
  29. reaxkit/data/alias.yaml +89 -0
  30. reaxkit/data/constants.yaml +27 -0
  31. reaxkit/data/reaxff_input_files_contents.yaml +186 -0
  32. reaxkit/data/reaxff_output_files_contents.yaml +301 -0
  33. reaxkit/data/units.yaml +38 -0
  34. reaxkit/help/__init__.py +0 -0
  35. reaxkit/help/help_index_loader.py +531 -0
  36. reaxkit/help/introspection_utils.py +131 -0
  37. reaxkit/io/__init__.py +0 -0
  38. reaxkit/io/base_handler.py +165 -0
  39. reaxkit/io/generators/__init__.py +0 -0
  40. reaxkit/io/generators/control_generator.py +123 -0
  41. reaxkit/io/generators/eregime_generator.py +341 -0
  42. reaxkit/io/generators/geo_generator.py +967 -0
  43. reaxkit/io/generators/trainset_generator.py +1758 -0
  44. reaxkit/io/generators/tregime_generator.py +113 -0
  45. reaxkit/io/generators/vregime_generator.py +164 -0
  46. reaxkit/io/generators/xmolout_generator.py +304 -0
  47. reaxkit/io/handlers/__init__.py +0 -0
  48. reaxkit/io/handlers/control_handler.py +209 -0
  49. reaxkit/io/handlers/eregime_handler.py +122 -0
  50. reaxkit/io/handlers/ffield_handler.py +812 -0
  51. reaxkit/io/handlers/fort13_handler.py +123 -0
  52. reaxkit/io/handlers/fort57_handler.py +143 -0
  53. reaxkit/io/handlers/fort73_handler.py +145 -0
  54. reaxkit/io/handlers/fort74_handler.py +155 -0
  55. reaxkit/io/handlers/fort76_handler.py +195 -0
  56. reaxkit/io/handlers/fort78_handler.py +142 -0
  57. reaxkit/io/handlers/fort79_handler.py +227 -0
  58. reaxkit/io/handlers/fort7_handler.py +264 -0
  59. reaxkit/io/handlers/fort99_handler.py +128 -0
  60. reaxkit/io/handlers/geo_handler.py +224 -0
  61. reaxkit/io/handlers/molfra_handler.py +184 -0
  62. reaxkit/io/handlers/params_handler.py +137 -0
  63. reaxkit/io/handlers/summary_handler.py +135 -0
  64. reaxkit/io/handlers/trainset_handler.py +658 -0
  65. reaxkit/io/handlers/vels_handler.py +293 -0
  66. reaxkit/io/handlers/xmolout_handler.py +174 -0
  67. reaxkit/utils/__init__.py +0 -0
  68. reaxkit/utils/alias.py +219 -0
  69. reaxkit/utils/cache.py +77 -0
  70. reaxkit/utils/constants.py +75 -0
  71. reaxkit/utils/equation_of_states.py +96 -0
  72. reaxkit/utils/exceptions.py +27 -0
  73. reaxkit/utils/frame_utils.py +175 -0
  74. reaxkit/utils/log.py +43 -0
  75. reaxkit/utils/media/__init__.py +0 -0
  76. reaxkit/utils/media/convert.py +90 -0
  77. reaxkit/utils/media/make_video.py +91 -0
  78. reaxkit/utils/media/plotter.py +812 -0
  79. reaxkit/utils/numerical/__init__.py +0 -0
  80. reaxkit/utils/numerical/extrema_finder.py +96 -0
  81. reaxkit/utils/numerical/moving_average.py +103 -0
  82. reaxkit/utils/numerical/numerical_calcs.py +75 -0
  83. reaxkit/utils/numerical/signal_ops.py +135 -0
  84. reaxkit/utils/path.py +55 -0
  85. reaxkit/utils/units.py +104 -0
  86. reaxkit/webui/__init__.py +0 -0
  87. reaxkit/webui/app.py +0 -0
  88. reaxkit/webui/components.py +0 -0
  89. reaxkit/webui/layouts.py +0 -0
  90. reaxkit/webui/utils.py +0 -0
  91. reaxkit/workflows/__init__.py +0 -0
  92. reaxkit/workflows/composed/__init__.py +0 -0
  93. reaxkit/workflows/composed/coordination_workflow.py +393 -0
  94. reaxkit/workflows/composed/electrostatics_workflow.py +587 -0
  95. reaxkit/workflows/composed/xmolout_fort7_workflow.py +343 -0
  96. reaxkit/workflows/meta/__init__.py +0 -0
  97. reaxkit/workflows/meta/help_workflow.py +136 -0
  98. reaxkit/workflows/meta/introspection_workflow.py +235 -0
  99. reaxkit/workflows/meta/make_video_workflow.py +61 -0
  100. reaxkit/workflows/meta/plotter_workflow.py +601 -0
  101. reaxkit/workflows/per_file/__init__.py +0 -0
  102. reaxkit/workflows/per_file/control_workflow.py +110 -0
  103. reaxkit/workflows/per_file/eregime_workflow.py +267 -0
  104. reaxkit/workflows/per_file/ffield_workflow.py +390 -0
  105. reaxkit/workflows/per_file/fort13_workflow.py +86 -0
  106. reaxkit/workflows/per_file/fort57_workflow.py +137 -0
  107. reaxkit/workflows/per_file/fort73_workflow.py +151 -0
  108. reaxkit/workflows/per_file/fort74_workflow.py +88 -0
  109. reaxkit/workflows/per_file/fort76_workflow.py +188 -0
  110. reaxkit/workflows/per_file/fort78_workflow.py +135 -0
  111. reaxkit/workflows/per_file/fort79_workflow.py +314 -0
  112. reaxkit/workflows/per_file/fort7_workflow.py +592 -0
  113. reaxkit/workflows/per_file/fort83_workflow.py +60 -0
  114. reaxkit/workflows/per_file/fort99_workflow.py +223 -0
  115. reaxkit/workflows/per_file/geo_workflow.py +554 -0
  116. reaxkit/workflows/per_file/molfra_workflow.py +577 -0
  117. reaxkit/workflows/per_file/params_workflow.py +135 -0
  118. reaxkit/workflows/per_file/summary_workflow.py +161 -0
  119. reaxkit/workflows/per_file/trainset_workflow.py +356 -0
  120. reaxkit/workflows/per_file/tregime_workflow.py +79 -0
  121. reaxkit/workflows/per_file/vels_workflow.py +309 -0
  122. reaxkit/workflows/per_file/vregime_workflow.py +75 -0
  123. reaxkit/workflows/per_file/xmolout_workflow.py +678 -0
  124. reaxkit-1.0.0.dist-info/METADATA +128 -0
  125. reaxkit-1.0.0.dist-info/RECORD +130 -0
  126. reaxkit-1.0.0.dist-info/WHEEL +5 -0
  127. reaxkit-1.0.0.dist-info/entry_points.txt +2 -0
  128. reaxkit-1.0.0.dist-info/licenses/AUTHORS.md +20 -0
  129. reaxkit-1.0.0.dist-info/licenses/LICENSE +21 -0
  130. reaxkit-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,106 @@
1
+ """
2
+ fort.57 (geometry / convergence) analysis utilities.
3
+
4
+ This module provides helpers for extracting selected convergence and
5
+ geometry-related quantities from a parsed ReaxFF ``fort.57`` file
6
+ via ``Fort57Handler``.
7
+
8
+ Typical use cases include:
9
+
10
+ - selecting canonical fort.57 columns with alias support
11
+ - exporting convergence metrics such as RMSG or potential energy
12
+ - attaching a geometry descriptor for multi-structure workflows
13
+ """
14
+
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Sequence
19
+
20
+ import pandas as pd
21
+
22
+ from reaxkit.io.handlers.fort57_handler import Fort57Handler
23
+ from reaxkit.utils.alias import normalize_choice, resolve_alias_from_columns
24
+
25
+
26
+ _F57_CANONICAL = ("iter", "E_pot", "T", "T_set", "RMSG", "nfc")
27
+
28
+
29
+ def get_fort57_data(
30
+ *,
31
+ fort57_handler: Fort57Handler,
32
+ cols: Sequence[str] | None = None,
33
+ include_geo_descriptor: bool = False,
34
+ ) -> pd.DataFrame:
35
+ """
36
+ Extract selected columns from a ``fort.57`` file as a DataFrame.
37
+
38
+ Works on
39
+ --------
40
+ Fort57Handler — ``fort.57``
41
+
42
+ Parameters
43
+ ----------
44
+ fort57_handler : Fort57Handler
45
+ Parsed ``fort.57`` handler.
46
+ cols : sequence of str, optional
47
+ Columns to extract using canonical names or aliases
48
+ (e.g. ``iter``, ``E_pot``, ``T``, ``T_set``, ``RMSG``, ``nfc``).
49
+ If None, all available columns are returned.
50
+ include_geo_descriptor : bool, default=False
51
+ If True, prepend a ``geo_descriptor`` column identifying
52
+ the geometry associated with this data.
53
+
54
+ Returns
55
+ -------
56
+ pandas.DataFrame
57
+ Table containing the requested columns. Column names are
58
+ normalized to canonical fort.57 keys.
59
+
60
+ Examples
61
+ --------
62
+ >>> from reaxkit.io.handlers.fort57_handler import Fort57Handler
63
+ >>> from reaxkit.analysis.per_file.fort57_analyzer import get_fort57_data
64
+ >>> h = Fort57Handler("fort.57")
65
+ >>> df = get_fort57_data(fort57_handler=h, cols=["iter", "RMSG"])
66
+ """
67
+ df = fort57_handler.dataframe()
68
+
69
+ # default: all canonical columns (if present)
70
+ if cols is None or len(cols) == 0:
71
+ out = df.copy()
72
+ else:
73
+ # 1) Normalize user requests to canonical keys (via alias.py)
74
+ wanted_canon = [normalize_choice(c, domain="fort57.md") for c in cols]
75
+
76
+ # 2) Resolve those canonical keys to actual dataframe columns
77
+ # (usually identical, but this keeps it robust)
78
+ resolved_cols: list[str] = []
79
+ available = list(df.columns)
80
+
81
+ for canon in wanted_canon:
82
+ if canon not in _F57_CANONICAL:
83
+ raise ValueError(
84
+ f"Unknown fort.57 column '{canon}'. "
85
+ f"Allowed: {', '.join(_F57_CANONICAL)}"
86
+ )
87
+
88
+ actual = resolve_alias_from_columns(available, canon)
89
+ if actual is None:
90
+ raise KeyError(
91
+ f"Column '{canon}' not found (and no alias matched). "
92
+ f"Available: {', '.join(available)}"
93
+ )
94
+ resolved_cols.append(actual)
95
+
96
+ out = df.loc[:, resolved_cols].copy()
97
+
98
+ # Optional: rename resolved columns back to canonical names
99
+ # so downstream code always sees iter/E_pot/T/T_set/RMSG/nfc
100
+ rename_map = dict(zip(resolved_cols, wanted_canon))
101
+ out = out.rename(columns=rename_map)
102
+
103
+ if include_geo_descriptor:
104
+ out.insert(0, "geo_descriptor", fort57_handler.geo_descriptor)
105
+
106
+ return out
@@ -0,0 +1,61 @@
1
+ """
2
+ Energy output analysis utilities (fort.73, energylog, fort.58).
3
+
4
+ This module provides a lightweight interface for accessing energy-related
5
+ output written by ReaxFF during MD, MM, or optimization runs. It supports
6
+ ``fort.73``, ``energylog``, and ``fort.58`` files via a common handler interface.
7
+
8
+ Typical use cases include:
9
+
10
+ - extracting selected energy terms versus iteration
11
+ - exporting energy components for plotting or post-processing
12
+ - working uniformly with fort.73, energylog, and fort.58 outputs
13
+ """
14
+
15
+
16
+ from __future__ import annotations
17
+ from typing import List, Optional
18
+ import pandas as pd
19
+
20
+ from reaxkit.io.base_handler import BaseHandler
21
+
22
+
23
+ def get_fort73_data(handler: BaseHandler, columns: Optional[List[str]] = None) -> pd.DataFrame:
24
+ """
25
+ Extract energy data from a ReaxFF energy output file as a DataFrame.
26
+
27
+ Works on
28
+ --------
29
+ Fort73Handler / EnergylogHandler / Fort58Handler — ``fort.73``, ``energylog``, ``fort.58``
30
+
31
+ Parameters
32
+ ----------
33
+ handler : TemplateHandler
34
+ Parsed handler instance for ``fort.73``, ``energylog``, or ``fort.58``.
35
+ columns : list[str], optional
36
+ Energy columns to extract (e.g. ``["iter", "Ebond", "Evdw"]``).
37
+ If None, all available columns are returned.
38
+
39
+ Returns
40
+ -------
41
+ pandas.DataFrame
42
+ Energy table indexed by iteration, containing the requested
43
+ energy components.
44
+
45
+ Examples
46
+ --------
47
+ >>> from reaxkit.io.handlers.fort73_handler import Fort73Handler
48
+ >>> from reaxkit.analysis.per_file.fort73_analyzer import get_fort73_data
49
+ >>> h = Fort73Handler("fort.73")
50
+ >>> df = get_fort73_data(h, columns=["iter", "Ebond", "Evdw"])
51
+ """
52
+ df = handler.dataframe().copy()
53
+
54
+ if columns is not None:
55
+ # Validate requested columns
56
+ missing = set(columns) - set(df.columns)
57
+ if missing:
58
+ raise KeyError(f"Requested columns not found in fort.73 DataFrame: {missing}")
59
+ df = df[columns]
60
+
61
+ return df
@@ -0,0 +1,65 @@
1
+ """
2
+ fort.74 (thermodynamic summary) analysis utilities.
3
+
4
+ This module provides helpers for accessing and sorting thermodynamic
5
+ summary data produced by ReaxFF in ``fort.74`` files via ``Fort74Handler``.
6
+
7
+ Typical use cases include:
8
+
9
+ - retrieving formation energies, volumes, or densities
10
+ - sorting structures by energy or iteration
11
+ - exporting fort.74 data for post-processing or plotting
12
+ """
13
+
14
+
15
+ from __future__ import annotations
16
+ import pandas as pd
17
+ from typing import Optional
18
+
19
+ from reaxkit.io.handlers.fort74_handler import Fort74Handler
20
+
21
+
22
+ def get_fort74_data(
23
+ handler: Fort74Handler,
24
+ *,
25
+ sort: Optional[str] = None,
26
+ ascending: bool = True
27
+ ) -> pd.DataFrame:
28
+ """
29
+ Retrieve thermodynamic summary data from a ``fort.74`` file.
30
+
31
+ Works on
32
+ --------
33
+ Fort74Handler — ``fort.74``
34
+
35
+ Parameters
36
+ ----------
37
+ handler : Fort74Handler
38
+ Parsed ``fort.74`` handler.
39
+ sort : str, optional
40
+ Column name or alias to sort by (e.g. ``Emin``, ``Hf``, ``iter``).
41
+ If None, rows are returned in file order.
42
+ ascending : bool, default=True
43
+ Sort order when ``sort`` is specified.
44
+
45
+ Returns
46
+ -------
47
+ pandas.DataFrame
48
+ Table containing thermodynamic and structural summary quantities
49
+ from ``fort.74``.
50
+
51
+ Examples
52
+ --------
53
+ >>> from reaxkit.io.handlers.fort74_handler import Fort74Handler
54
+ >>> from reaxkit.analysis.per_file.fort74_analyzer import get_fort74_data
55
+ >>> h = Fort74Handler("fort.74")
56
+ >>> df = get_fort74_data(h, sort="Emin")
57
+ """
58
+ df = handler.dataframe().copy()
59
+
60
+ # No sorting → return as-is
61
+ if not sort:
62
+ return df
63
+
64
+ # Sort and return
65
+ return df.sort_values(by=sort, ascending=ascending).reset_index(drop=True)
@@ -0,0 +1,191 @@
1
+ """
2
+ fort.76 (restraint monitor) analysis utilities.
3
+
4
+ This module provides helpers for extracting and organizing restraint-related
5
+ quantities written by ReaxFF into ``fort.76`` files via ``Fort76Handler``.
6
+
7
+ Typical use cases include:
8
+
9
+ - extracting target and actual values for specific restraints
10
+ - selecting subsets of restraint columns with alias support
11
+ - preparing clean tables for plotting or downstream analysis
12
+ """
13
+
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import List, Sequence
18
+ import pandas as pd
19
+
20
+ from reaxkit.io.handlers.fort76_handler import Fort76Handler
21
+ from reaxkit.utils.alias import _resolve_alias, available_keys
22
+
23
+
24
+ # ---------- Internal helpers ----------
25
+
26
+ def _resolve_fort76_column(handler: Fort76Handler, requested: str) -> str:
27
+ """
28
+ Resolve a user-requested column name to a canonical fort.76 DataFrame column.
29
+
30
+ Resolution order:
31
+ 1) Restraint columns: "restraint 1 target", "r1_actual", etc.
32
+ 2) Standard columns via alias.py: iter, E_res, E_pot
33
+ 3) Exact column name fallback
34
+ """
35
+ df = handler.dataframe()
36
+ cols = list(df.columns)
37
+
38
+ key = requested.strip().lower()
39
+
40
+ # ---- 1) restraint columns (simple parsing, no regex) ----
41
+ # accepted file_templates:
42
+ # "restraint 1 target value"
43
+ # "restraint 1 actual"
44
+ # "r1_target"
45
+ # "r2 actual"
46
+ if "restraint" in key or key.startswith("r"):
47
+ parts = key.replace("_", " ").split()
48
+
49
+ # find index
50
+ idx = None
51
+ for p in parts:
52
+ if p.isdigit():
53
+ idx = int(p)
54
+ break
55
+
56
+ if idx is not None:
57
+ if "target" in parts:
58
+ col = f"r{idx}_target"
59
+ elif "actual" in parts:
60
+ col = f"r{idx}_actual"
61
+ else:
62
+ raise KeyError(
63
+ f"Restraint column must specify target or actual: '{requested}'"
64
+ )
65
+
66
+ if col not in cols:
67
+ raise KeyError(
68
+ f"Resolved '{requested}' → '{col}', but column not found. "
69
+ f"Available restraint columns: {[c for c in cols if c.startswith('r')]}"
70
+ )
71
+ return col
72
+
73
+ # ---- 2) standard columns via alias.py ----
74
+ try:
75
+ return _resolve_alias(df, requested)
76
+ except KeyError:
77
+ pass
78
+
79
+ # ---- 3) exact match fallback ----
80
+ if requested in cols:
81
+ return requested
82
+
83
+ raise KeyError(
84
+ f"Could not resolve requested column '{requested}'. "
85
+ f"Available columns / aliases: {available_keys(cols)}"
86
+ )
87
+
88
+
89
+ # ---------- Public API ----------
90
+
91
+ def get_fort76_data(
92
+ handler: Fort76Handler,
93
+ columns: Sequence[str],
94
+ dropna_rows: bool = False,
95
+ copy: bool = True,
96
+ ) -> pd.DataFrame:
97
+ """
98
+ Extract user-requested columns from a ``fort.76`` file.
99
+
100
+ Works on
101
+ --------
102
+ Fort76Handler — ``fort.76``
103
+
104
+ Parameters
105
+ ----------
106
+ handler : Fort76Handler
107
+ Parsed ``fort.76`` handler.
108
+ columns : sequence of str
109
+ Column names to extract. Aliases are supported, including
110
+ restraint specifications such as ``"restraint 1 target"``
111
+ or ``"r2_actual"``.
112
+ dropna_rows : bool, default=False
113
+ If True, drop rows where all selected non-``iter`` columns are NaN.
114
+ copy : bool, default=True
115
+ If True, return a copy of the DataFrame.
116
+
117
+ Returns
118
+ -------
119
+ pandas.DataFrame
120
+ Table containing only the requested columns, in the order specified.
121
+
122
+ Examples
123
+ --------
124
+ >>> from reaxkit.io.handlers.fort76_handler import Fort76Handler
125
+ >>> from reaxkit.analysis.per_file.fort76_analyzer import get_fort76_data
126
+ >>> h = Fort76Handler("fort.76")
127
+ >>> df = get_fort76_data(
128
+ ... h,
129
+ ... ["iter", "restraint 1 target value", "restraint 1 actual value"]
130
+ ... )
131
+ """
132
+ df = handler.dataframe()
133
+
134
+ resolved: List[str] = [
135
+ _resolve_fort76_column(handler, col) for col in columns
136
+ ]
137
+
138
+ out = df.loc[:, resolved]
139
+
140
+ if dropna_rows:
141
+ non_iter = [c for c in out.columns if c.lower() != "iter"]
142
+ if non_iter:
143
+ out = out.dropna(axis=0, how="all", subset=non_iter)
144
+
145
+ return out.copy() if copy else out
146
+
147
+
148
+ def get_fort76_restraint_pairs(
149
+ handler: Fort76Handler,
150
+ restraint_index: int,
151
+ include_iter: bool = True,
152
+ ) -> pd.DataFrame:
153
+ """
154
+ Extract target and actual values for a single restraint.
155
+
156
+ Works on
157
+ --------
158
+ Fort76Handler — ``fort.76``
159
+
160
+ Parameters
161
+ ----------
162
+ handler : Fort76Handler
163
+ Parsed ``fort.76`` handler.
164
+ restraint_index : int
165
+ Index of the restraint (1-based, as in the ReaxFF input).
166
+ include_iter : bool, default=True
167
+ If True, include the ``iter`` column.
168
+
169
+ Returns
170
+ -------
171
+ pandas.DataFrame
172
+ Table with columns ``["iter", "rN_target", "rN_actual"]`` if
173
+ ``include_iter=True``, otherwise ``["rN_target", "rN_actual"]``.
174
+
175
+ Examples
176
+ --------
177
+ >>> from reaxkit.io.handlers.fort76_handler import Fort76Handler
178
+ >>> from reaxkit.analysis.per_file.fort76_analyzer import get_fort76_restraint_pairs
179
+ >>> h = Fort76Handler("fort.76")
180
+ >>> df = get_fort76_restraint_pairs(h, restraint_index=1)
181
+ """
182
+ req: List[str] = []
183
+ if include_iter:
184
+ req.append("iter")
185
+
186
+ req += [
187
+ f"restraint {restraint_index} target value",
188
+ f"restraint {restraint_index} actual value",
189
+ ]
190
+
191
+ return get_fort76_data(handler, req)
@@ -0,0 +1,154 @@
1
+ """
2
+ fort.78 (external field schedule) analysis utilities.
3
+
4
+ This module provides helpers for extracting and aligning electric-field
5
+ schedule data written by ReaxFF into ``fort.78`` files via ``Fort78Handler``.
6
+
7
+ Typical use cases include:
8
+
9
+ - extracting electric-field components versus iteration
10
+ - matching field values to other outputs sampled at ``iout2`` frequency
11
+ - preparing aligned field profiles for plotting with xmolout or summaries
12
+ """
13
+
14
+
15
+ from __future__ import annotations
16
+ from typing import Sequence
17
+ import pandas as pd
18
+
19
+ from reaxkit.io.handlers.fort78_handler import Fort78Handler
20
+ from reaxkit.utils.alias import resolve_alias_from_columns, load_default_alias_map
21
+ from reaxkit.io.handlers.control_handler import ControlHandler
22
+ from reaxkit.analysis.per_file.control_analyzer import get_control_data
23
+
24
+ def get_fort78_data(handler: Fort78Handler, variables: str | Sequence[str]) -> pd.DataFrame:
25
+ """Extract iteration versus one or more variables from a ``fort.78`` file.
26
+
27
+ Works on
28
+ --------
29
+ Fort78Handler — ``fort.78``
30
+
31
+ Parameters
32
+ ----------
33
+ handler : Fort78Handler
34
+ Parsed ``fort.78`` handler.
35
+ variables : str or sequence of str
36
+ Field variable name(s) to extract. Aliases are supported
37
+ (e.g. ``E_field_z``, ``field_z``). Output columns are named
38
+ exactly as requested.
39
+
40
+ Returns
41
+ -------
42
+ pandas.DataFrame
43
+ Table with columns ``["iter", <variables...>]`` where
44
+ ``<variables>`` correspond to the requested names.
45
+
46
+ Examples
47
+ --------
48
+ >>> from reaxkit.io.handlers.fort78_handler import Fort78Handler
49
+ >>> from reaxkit.analysis.per_file.fort78_analyzer import get_fort78_data
50
+ >>> h = Fort78Handler("fort.78")
51
+ >>> df = get_fort78_data(h, variables=["E_field_z", "E_field_x"])
52
+ """
53
+ df = handler.dataframe()
54
+ cols = list(df.columns)
55
+
56
+ if isinstance(variables, str):
57
+ variables = [variables]
58
+
59
+ # Resolve iter, then always expose as 'iter'
60
+ iter_hit = resolve_alias_from_columns(cols, "iter", load_default_alias_map())
61
+ if iter_hit is None:
62
+ raise KeyError(f"'iter' column not found. Available: {cols}")
63
+
64
+ out = df[[iter_hit]].copy()
65
+ if iter_hit != "iter":
66
+ out = out.rename(columns={iter_hit: "iter"})
67
+
68
+ # Resolve and rename each requested var to the *requested name*
69
+ for var in variables:
70
+ hit = resolve_alias_from_columns(cols, var, load_default_alias_map())
71
+ if hit is None:
72
+ raise KeyError(f"Variable '{var}' not found. Available: {cols}")
73
+ # attach numeric-ified column using the requested alias name
74
+ ser = pd.to_numeric(df[hit], errors="coerce")
75
+ out[var] = ser
76
+
77
+ # Ensure iter is numeric/int
78
+ out["iter"] = pd.to_numeric(out["iter"], errors="coerce").astype("Int64")
79
+ out = out.dropna(subset=["iter"])
80
+ out["iter"] = out["iter"].astype(int)
81
+
82
+ return out
83
+
84
+ # ----------------------------------------------------------------------------------------
85
+ # matching electric fields data to iout2, which is used to get plots of summary,
86
+ # xmolout, polarization, or etc. along with electric field profile
87
+ # ----------------------------------------------------------------------------------------
88
+
89
+ def match_electric_field_to_iout2(
90
+ f78: Fort78Handler,
91
+ ctrl: ControlHandler,
92
+ target_iters: Sequence[int],
93
+ field_var: str = "E_field_z",
94
+ ) -> pd.Series:
95
+ """
96
+ Match electric-field values from ``fort.78`` to target iterations.
97
+
98
+ The matching follows a piecewise-constant rule:
99
+ for each target iteration, the last available fort.78 value with
100
+ ``iter <= target_iter`` is used. By convention, ``iter = 0`` maps to 0.0.
101
+
102
+ Works on
103
+ --------
104
+ Fort78Handler + ControlHandler — ``fort.78`` + ``control``
105
+
106
+ Parameters
107
+ ----------
108
+ f78 : Fort78Handler
109
+ Parsed ``fort.78`` handler providing the field schedule.
110
+ ctrl : ControlHandler
111
+ Parsed control handler (used to access ``iout2`` for consistency).
112
+ target_iters : sequence of int
113
+ Iteration numbers to which the electric field should be matched.
114
+ field_var : str, default="E_field_z"
115
+ Electric-field component to use (aliases supported).
116
+
117
+ Returns
118
+ -------
119
+ pandas.Series
120
+ Series indexed by ``target_iters`` containing matched electric-field
121
+ values (same units as ``fort.78``, typically V/Å).
122
+
123
+ Examples
124
+ --------
125
+ >>> field = match_electric_field_to_iout2(
126
+ ... f78, ctrl, target_iters=[0, 80, 160], field_var="E_field_z"
127
+ ... )
128
+ """
129
+ # Access iout2 from control (not strictly needed for the matching logic,
130
+ # but useful for consistency/sanity checks with xmolout/summary output rate).
131
+ iout2 = get_control_data(ctrl, "iout2", section="md", default=1)
132
+ _ = iout2 # currently not used explicitly, but kept for future use/logging
133
+
134
+ df_E = get_fort78_data(f78, variables=field_var) # columns: ['iter', field_var]
135
+ if df_E.empty:
136
+ raise ValueError("fort.78 has no usable data.")
137
+
138
+ df_E = df_E.sort_values("iter").reset_index(drop=True)
139
+ mapping = pd.Series(df_E[field_var].values, index=df_E["iter"].values)
140
+ sorted_iters = list(mapping.index)
141
+
142
+ out_vals = []
143
+ for it in target_iters:
144
+ it = int(it)
145
+ if it == 0:
146
+ out_vals.append(0.0)
147
+ continue
148
+ # all fort.78 iters <= this iter
149
+ valid = [f for f in sorted_iters if f <= it]
150
+ if not valid:
151
+ raise ValueError(f"No fort.78 iteration ≤ {it}")
152
+ out_vals.append(float(mapping[max(valid)]))
153
+
154
+ return pd.Series(out_vals, index=list(target_iters), name=field_var)
@@ -0,0 +1,83 @@
1
+ """
2
+ fort.79 (parameter sensitivity) analysis utilities.
3
+
4
+ This module provides helpers for analyzing force-field parameter
5
+ sensitivity information written by ReaxFF into ``fort.79`` files
6
+ via ``Fort79Handler``.
7
+
8
+ Typical use cases include:
9
+
10
+ - computing relative sensitivities of force-field error to parameter changes
11
+ - identifying parameters with the strongest impact on total error
12
+ - post-processing sensitivity tables for ranking or visualization
13
+ """
14
+
15
+
16
+ import pandas as pd
17
+ from reaxkit.io.handlers.fort79_handler import Fort79Handler
18
+
19
+
20
+ def get_fort79_data_with_diff_sensitivities(handler: Fort79Handler) -> pd.DataFrame:
21
+ """
22
+ Compute relative force-field error sensitivities from a ``fort.79`` file.
23
+
24
+ Sensitivities are computed by normalizing ``diff1``, ``diff2``, and
25
+ (optionally) ``diff4`` by ``diff3``, which represents the error obtained
26
+ using the current value of each parameter.
27
+
28
+ Works on
29
+ --------
30
+ Fort79Handler — ``fort.79``
31
+
32
+ Parameters
33
+ ----------
34
+ handler : Fort79Handler
35
+ Parsed ``fort.79`` handler containing sensitivity data.
36
+
37
+ Returns
38
+ -------
39
+ pandas.DataFrame
40
+ Sensitivity table with columns:
41
+ - ``identifier``: parameter identifier
42
+ - ``sensitivity1/3``: ``diff1 / diff3``
43
+ - ``sensitivity2/3``: ``diff2 / diff3``
44
+ - ``sensitivity4/3``: ``diff4 / diff3`` (NaN if ``diff4`` is absent)
45
+ - ``min_sensitivity``: minimum sensitivity across available diffs
46
+ - ``max_sensitivity``: maximum sensitivity across available diffs
47
+
48
+ Examples
49
+ --------
50
+ >>> from reaxkit.io.handlers.fort79_handler import Fort79Handler
51
+ >>> from reaxkit.analysis.per_file.fort79_analyzer import get_fort79_data_with_diff_sensitivities
52
+ >>> h = Fort79Handler("fort.79")
53
+ >>> df = get_fort79_data_with_diff_sensitivities(h)
54
+ """
55
+ df = handler.dataframe() if hasattr(handler, "dataframe") else handler._parse()[0]
56
+
57
+ # ensure numeric and safe division
58
+ result = pd.DataFrame()
59
+
60
+ result['identider'] = df['identifier']
61
+
62
+ if "diff1" in df and "diff3" in df:
63
+ result["sensitivity1/3"] = df["diff1"] / df["diff3"]
64
+ else:
65
+ result["sensitivity1/3"] = pd.Series(dtype=float)
66
+
67
+ if "diff2" in df and "diff3" in df:
68
+ result["sensitivity2/3"] = df["diff2"] / df["diff3"]
69
+ else:
70
+ result["sensitivity2/3"] = pd.Series(dtype=float)
71
+
72
+ # optional diff4 handling
73
+ if "diff4" in df and "diff3" in df:
74
+ result["sensitivity4/3"] = df["diff4"] / df["diff3"]
75
+ else:
76
+ result["sensitivity4/3"] = pd.Series([float("nan")] * len(result))
77
+
78
+ result["min_sensitivity"] = result[["sensitivity1/3", "sensitivity2/3", "sensitivity4/3"]].min(axis=1)
79
+ result["max_sensitivity"] = result[["sensitivity1/3", "sensitivity2/3", "sensitivity4/3"]].max(axis=1)
80
+
81
+ return result
82
+
83
+