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,151 @@
1
+ """
2
+ Energy-output workflow for ReaxKit (fort.73 / energylog / fort.58).
3
+
4
+ This workflow provides tools for reading, visualizing, and exporting energy
5
+ and thermodynamic data produced by ReaxFF simulations, as stored in
6
+ `fort.73`, `energylog`, or `fort.58` files.
7
+
8
+ It supports:
9
+ - Extracting individual or all available energy terms (e.g. Ebond, Eangle,
10
+ Etot) as functions of iteration, frame index, or physical time.
11
+ - Converting the x-axis between iteration, frame, and time using the
12
+ associated control file.
13
+ - Plotting selected energy components or saving them as image files.
14
+ - Exporting energy data to CSV for downstream analysis or comparison
15
+ across simulation runs.
16
+
17
+ The workflow is designed for rapid inspection of ReaxFF energy evolution,
18
+ stability, and convergence behavior.
19
+ """
20
+
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse, os
25
+ import pandas as pd
26
+
27
+ from reaxkit.io.handlers.fort73_handler import Fort73Handler
28
+ from reaxkit.analysis.per_file.fort73_analyzer import get_fort73_data
29
+ from reaxkit.utils.media.convert import convert_xaxis
30
+ from reaxkit.utils.media.plotter import single_plot
31
+ from reaxkit.utils.path import resolve_output_path
32
+
33
+ def _fort73_get_task(args: argparse.Namespace) -> int:
34
+ DEFAULT_FILES = {
35
+ "fort73": "fort.73",
36
+ "energylog": "energylog",
37
+ "fort58": "fort.58",
38
+ }
39
+
40
+ default_file = DEFAULT_FILES.get(args.kind, "fort.73")
41
+
42
+ file_path = args.file or default_file
43
+
44
+ handler = Fort73Handler(file_path)
45
+ df = get_fort73_data(handler)
46
+
47
+ if "iter" not in df.columns:
48
+ raise KeyError("Column 'iter' not found in fort.73 DataFrame.")
49
+
50
+ # --- X-axis conversion ---
51
+ iters = df["iter"].to_numpy()
52
+ x_vals, x_label = convert_xaxis(iters, args.xaxis, control_file=args.control)
53
+
54
+ # --- Y columns selection ---
55
+ if args.yaxis.lower() == "all":
56
+ y_cols = [c for c in df.columns if c != "iter"]
57
+ if not y_cols:
58
+ raise ValueError("No energy columns found in fort.73 DataFrame.")
59
+ else:
60
+ if args.yaxis not in df.columns:
61
+ raise KeyError(f"Requested y-axis column '{args.yaxis}' not found in fort.73 DataFrame.")
62
+ y_cols = [args.yaxis]
63
+
64
+ workflow_name = args.kind
65
+
66
+ # --- Export CSV (optional, data only) ---
67
+ if args.export:
68
+ out = resolve_output_path(args.export, workflow_name)
69
+ export_df = pd.DataFrame({"x": x_vals})
70
+ for col in y_cols:
71
+ export_df[col] = df[col].values
72
+ export_df.to_csv(out, index=False)
73
+ print(f"[Done] Exported fort.73 data to {out}")
74
+
75
+ # --- Plot / Save (independent flags) ---
76
+ if args.plot or args.save:
77
+ save_base = None
78
+ base_is_dir = False
79
+
80
+ if args.save:
81
+ # Resolve to reaxkit_out/... using same helper as export
82
+ save_base = resolve_output_path(args.save, workflow_name)
83
+
84
+ # Decide if save_base acts like a directory or a file
85
+ if len(y_cols) > 1:
86
+ # Multiple columns → treat as directory unless clearly a file name
87
+ base_is_dir = os.path.isdir(save_base) or "." not in os.path.basename(save_base)
88
+ else:
89
+ base_is_dir = os.path.isdir(save_base)
90
+
91
+ if base_is_dir:
92
+ os.makedirs(save_base, exist_ok=True)
93
+ else:
94
+ parent = os.path.dirname(save_base)
95
+ if parent:
96
+ os.makedirs(parent, exist_ok=True)
97
+
98
+ for col in y_cols:
99
+ y = df[col].values
100
+
101
+ # Decide per-column save path (or None)
102
+ save_path = None
103
+ if save_base:
104
+ if len(y_cols) == 1:
105
+ # Single column: allow explicit filename or directory
106
+ if base_is_dir:
107
+ save_path = os.path.join(save_base, f"{col}.png")
108
+ else:
109
+ save_path = save_base
110
+ else:
111
+ # Multiple columns: directory or filename with suffix
112
+ if base_is_dir:
113
+ save_path = os.path.join(save_base, f"{col}.png")
114
+ else:
115
+ root, ext = os.path.splitext(save_base)
116
+ if not ext:
117
+ ext = ".png"
118
+ save_path = f"{root}_{col}{ext}"
119
+
120
+ # single_plot handles both “show only” (save=None) and “save to file”
121
+ single_plot(
122
+ x_vals,
123
+ y,
124
+ title=f"{col} vs {x_label}",
125
+ xlabel=x_label,
126
+ ylabel=f"{col} (kcal/mole)",
127
+ save=save_path,
128
+ )
129
+
130
+ return 0
131
+
132
+
133
+ def register_tasks(subparsers: argparse._SubParsersAction) -> None:
134
+ p_get = subparsers.add_parser(
135
+ "get",
136
+ help="Extract and plot energy data from fort.73\n",
137
+ description=(
138
+ "Examples:\n"
139
+ " reaxkit fort73 get --yaxis Ebond --xaxis time --plot \n"
140
+ " reaxkit fort73 get --yaxis all --xaxis time --save reaxkit_outputs/fort73/ \n"
141
+ ),
142
+ formatter_class=argparse.RawTextHelpFormatter,
143
+ )
144
+ p_get.add_argument("--file", default=None, help="Path to fort.73 / energylog file")
145
+ p_get.add_argument("--yaxis", required=True, help="Energy column (e.g. Ebond) or 'all'")
146
+ p_get.add_argument("--xaxis", default="iter", choices=["iter", "frame", "time"], help="X-axis type")
147
+ p_get.add_argument("--control", default="control", help="Control file (used when xaxis=time)")
148
+ p_get.add_argument("--export", default=None, help="Path to export CSV (x + selected y columns)")
149
+ p_get.add_argument("--save", default=None, help="Path to save plot image (suffix _<col> if yaxis=all)")
150
+ p_get.add_argument("--plot", action="store_true", help="If set, generate plot(s).")
151
+ p_get.set_defaults(_run=_fort73_get_task)
@@ -0,0 +1,88 @@
1
+ """
2
+ fort.74 summary-table workflow for ReaxKit.
3
+
4
+ This workflow provides utilities for reading and exporting data from ReaxFF
5
+ `fort.74` files, which contain per-structure or per-configuration summary
6
+ quantities produced during force-field training or evaluation runs.
7
+
8
+ It supports:
9
+ - Exporting all available columns from a `fort.74` file to CSV.
10
+ - Selecting and exporting a single column (with optional alias resolution,
11
+ e.g. `Density` → `D`) while always preserving the structure identifier.
12
+ - Writing outputs to a standardized ReaxKit output directory for
13
+ reproducible data organization.
14
+
15
+ The workflow is designed for lightweight inspection and downstream analysis
16
+ of ReaxFF summary metrics such as energies, volumes, and densities.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+
23
+ from reaxkit.io.handlers.fort74_handler import Fort74Handler
24
+ from reaxkit.analysis.per_file.fort74_analyzer import get_fort74_data
25
+ from reaxkit.utils.path import resolve_output_path
26
+ from reaxkit.utils.alias import normalize_choice, resolve_alias_from_columns
27
+
28
+ def _get_task(args: argparse.Namespace) -> int:
29
+ handler = Fort74Handler(args.file)
30
+ df = get_fort74_data(handler)
31
+
32
+ col_raw = (args.col or "all").strip()
33
+
34
+ if col_raw != "all":
35
+ canonical = normalize_choice(col_raw) # e.g., "Density" -> "D"
36
+ resolved = resolve_alias_from_columns(df.columns, canonical)
37
+ if resolved is None:
38
+ raise SystemExit(
39
+ f"❌ Column '{col_raw}' not found (and no alias matched). "
40
+ f"Available: {', '.join(df.columns)}"
41
+ )
42
+
43
+ cols = []
44
+ if "identifier" in df.columns:
45
+ cols.append("identifier")
46
+ if resolved != "identifier":
47
+ cols.append(resolved)
48
+
49
+ out_df = df[cols].copy()
50
+
51
+ # Optional: make exported header match what user asked for
52
+ if resolved != col_raw:
53
+ out_df = out_df.rename(columns={resolved: col_raw})
54
+ else:
55
+ out_df = df
56
+
57
+ out_path = resolve_output_path(args.export, workflow="fort74.md")
58
+ out_df.to_csv(out_path, index=False)
59
+ print(f"[Done] Successfully exported the requested data to {out_path}")
60
+ return 0
61
+
62
+
63
+
64
+
65
+ def register_tasks(subparsers: argparse._SubParsersAction) -> None:
66
+ p = subparsers.add_parser(
67
+ "get",
68
+ help="Export one column or all columns from fort.74 to CSV.",
69
+ description=(
70
+ "Examples:\n"
71
+ " reaxkit fort74.md get --export fort74_all_data.csv\n"
72
+ " reaxkit fort74.md get --col Emin --export fort74_all_Emin_data.csv\n"
73
+ ),
74
+ formatter_class=argparse.RawTextHelpFormatter,
75
+ )
76
+
77
+ p.add_argument("--file", default="fort.74", help="Path to fort.74 file.")
78
+ p.add_argument("--col", default="all", help="Column to export, or 'all'.")
79
+ p.add_argument(
80
+ "--export",
81
+ required=True,
82
+ help=(
83
+ "CSV output path. If a bare filename is given, it will be saved under "
84
+ "reaxkit_outputs/fort74.md/ (via path.resolve_output_path)."
85
+ ),
86
+ )
87
+
88
+ p.set_defaults(_run=_get_task)
@@ -0,0 +1,188 @@
1
+ """
2
+ fort.76 restraint-analysis workflow for ReaxKit.
3
+
4
+ This workflow provides tools for reading, visualizing, and exporting data from
5
+ ReaxFF `fort.76` files, which record restraint targets and actual values during
6
+ MD or minimization runs.
7
+
8
+ It supports:
9
+ - Extracting and plotting a single restraint-related column versus iteration,
10
+ frame index, or physical time.
11
+ - Comparing restraint target and actual values for a selected restraint index
12
+ as a function of iteration, frame, or time.
13
+ - Converting the x-axis between iteration, frame, and time using the associated
14
+ control file.
15
+ - Saving plots to disk or exporting data to CSV using standardized output paths.
16
+
17
+ The workflow is intended for diagnosing restraint behavior, convergence, and
18
+ stability in constrained ReaxFF simulations.
19
+ """
20
+
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+
26
+ import pandas as pd
27
+
28
+ from reaxkit.io.handlers.fort76_handler import Fort76Handler
29
+ from reaxkit.analysis.per_file.fort76_analyzer import get_fort76_data, get_fort76_restraint_pairs
30
+ from reaxkit.utils.media.convert import convert_xaxis
31
+ from reaxkit.utils.media.plotter import single_plot
32
+ from reaxkit.utils.path import resolve_output_path
33
+
34
+
35
+ def _fort76_get_task(args: argparse.Namespace) -> int:
36
+ """
37
+ Handle: reaxkit fort76 get ...
38
+ Plot/save/export ONE column vs iter/frame/time (derived from iter).
39
+ """
40
+ handler = Fort76Handler(args.file)
41
+ handler._parse()
42
+
43
+ df = get_fort76_data(handler, ["iter", args.ycol], dropna_rows=True).copy()
44
+
45
+ xvals, xlabel = convert_xaxis(df["iter"].to_numpy(), args.xaxis, control_file=args.control)
46
+ x = pd.Series(xvals)
47
+
48
+ y_name = df.columns[1]
49
+ y = df[y_name]
50
+
51
+ workflow_name = args.kind # same pattern as fort13_workflow :contentReference[oaicite:4]{index=4}
52
+
53
+ # Export
54
+ if args.export:
55
+ out = resolve_output_path(args.export, workflow_name)
56
+ pd.DataFrame({xlabel: x, y_name: y}).to_csv(out, index=False)
57
+ print(f"[Done] Exported data to {out}.")
58
+
59
+ # Plot / Save
60
+ if args.plot:
61
+ single_plot(
62
+ x=x,
63
+ y=y,
64
+ title=args.title or f"{y_name} vs {args.xaxis}",
65
+ xlabel=args.xlabel or xlabel,
66
+ ylabel=args.ylabel or y_name,
67
+ save=None,
68
+ )
69
+ elif args.save:
70
+ out = resolve_output_path(args.save, workflow_name)
71
+ single_plot(
72
+ x=x,
73
+ y=y,
74
+ title=args.title or f"{y_name} vs {args.xaxis}",
75
+ xlabel=args.xlabel or xlabel,
76
+ ylabel=args.ylabel or y_name,
77
+ save=out,
78
+ )
79
+
80
+ if not (args.plot or args.save or args.export):
81
+ print("ℹ️ Nothing to do. Use --plot, --save <path>, --export <csv>.")
82
+ return 0
83
+
84
+
85
+ def _fort76_respair_task(args: argparse.Namespace) -> int:
86
+ """
87
+ Handle: reaxkit fort76 respair ...
88
+ Plot/save/export restraint target+actual vs iter/frame/time.
89
+ """
90
+ handler = Fort76Handler(args.file)
91
+ handler._parse()
92
+
93
+ df = get_fort76_restraint_pairs(handler, args.restraint, include_iter=True).copy()
94
+ target_col = df.columns[1]
95
+ actual_col = df.columns[2]
96
+
97
+ xvals, xlabel = convert_xaxis(df["iter"].to_numpy(), args.xaxis, control_file=args.control)
98
+ x = pd.Series(xvals)
99
+
100
+ workflow_name = args.kind
101
+
102
+ # Export
103
+ if args.export:
104
+ out = resolve_output_path(args.export, workflow_name)
105
+ pd.DataFrame(
106
+ {xlabel: x, target_col: df[target_col], actual_col: df[actual_col]}
107
+ ).to_csv(out, index=False)
108
+ print(f"Successfully exported data to {out}.")
109
+
110
+ # Plot / Save
111
+ if args.plot:
112
+ single_plot(
113
+ series=[
114
+ {"x": x, "y": df[target_col], "label": target_col},
115
+ {"x": x, "y": df[actual_col], "label": actual_col},
116
+ ],
117
+ title=args.title or f"Restraint {args.restraint}: target vs actual ({args.xaxis})",
118
+ xlabel=args.xlabel or xlabel,
119
+ ylabel=args.ylabel or "Value",
120
+ legend=True,
121
+ save=None,
122
+ )
123
+ elif args.save:
124
+ out = resolve_output_path(args.save, workflow_name)
125
+ single_plot(
126
+ series=[
127
+ {"x": x, "y": df[target_col], "label": target_col},
128
+ {"x": x, "y": df[actual_col], "label": actual_col},
129
+ ],
130
+ title=args.title or f"Restraint {args.restraint}: target vs actual ({args.xaxis})",
131
+ xlabel=args.xlabel or xlabel,
132
+ ylabel=args.ylabel or "Value",
133
+ legend=True,
134
+ save=out,
135
+ )
136
+
137
+ if not (args.plot or args.save or args.export):
138
+ print("ℹ️ Nothing to do. Use --plot, --save <path>, --export <csv>.")
139
+ return 0
140
+
141
+
142
+ def _add_common_fort76_io_args(
143
+ p: argparse.ArgumentParser,
144
+ *,
145
+ include_plot: bool = False,
146
+ ) -> None:
147
+ p.add_argument("--file", default="fort.76", help="Path to fort.76 file.")
148
+ p.add_argument("--xaxis", default="iter", choices=["iter", "frame", "time"])
149
+ p.add_argument("--control", default="control", help="Needed for --xaxis time.")
150
+ if include_plot:
151
+ p.add_argument("--plot", action="store_true", help="Show plot interactively.")
152
+ p.add_argument("--save", default=None, help="Path to save plot image.")
153
+ p.add_argument("--export", default=None, help="Path to export CSV data.")
154
+ p.add_argument("--title", default=None)
155
+ p.add_argument("--xlabel", default=None)
156
+ p.add_argument("--ylabel", default=None)
157
+
158
+
159
+ def register_tasks(subparsers: argparse._SubParsersAction) -> None:
160
+ # --- get ---
161
+ p = subparsers.add_parser(
162
+ "get",
163
+ help="Plot, export, or save one fort.76 column vs iter/frame/time.\n",
164
+ description=(
165
+ "Examples:\n"
166
+ " reaxkit fort76 get --ycol E_res --xaxis time --save E_res_vs_time.png\n"
167
+ " reaxkit fort76 get --ycol r1_actual --xaxis frame --export r1_actual_vs_frame.csv\n"
168
+ ),
169
+ formatter_class=argparse.RawTextHelpFormatter,
170
+ )
171
+ _add_common_fort76_io_args(p, include_plot=True)
172
+ p.add_argument("--ycol", required=True, help="Column to plot/export (aliases allowed).")
173
+ p.set_defaults(_run=_fort76_get_task)
174
+
175
+ # --- respair ---
176
+ p2 = subparsers.add_parser(
177
+ "respair",
178
+ help="Plot, export, or save restraint target+actual vs iter/frame/time.\n",
179
+ description=(
180
+ "Examples:\n"
181
+ " reaxkit fort76 respair --restraint 2 --xaxis time --control control --save r2_vs_time.png\n"
182
+ ),
183
+ formatter_class=argparse.RawTextHelpFormatter,
184
+ )
185
+ _add_common_fort76_io_args(p2, include_plot=True)
186
+ p2.add_argument("--restraint", type=int, required=True, help="Restraint index (1-based).")
187
+ p2.set_defaults(_run=_fort76_respair_task)
188
+
@@ -0,0 +1,135 @@
1
+ """
2
+ fort.78 electric-field output workflow for ReaxKit.
3
+
4
+ This workflow provides tools for reading, visualizing, and exporting data from
5
+ ReaxFF `fort.78` files, which record time-dependent electric-field–related
6
+ quantities during simulations with applied external fields.
7
+
8
+ It supports:
9
+ - Extracting a single electric-field or related scalar component (e.g.
10
+ `E_field_x`) as a function of iteration, frame index, or physical time.
11
+ - Converting the x-axis between iteration, frame, and time using the associated
12
+ control file when required.
13
+ - Plotting the selected quantity, saving figures to disk, or exporting the data
14
+ to CSV using standardized output paths.
15
+
16
+ The workflow is intended for analysis of electric-field protocols, field-driven
17
+ responses, and post-processing of ReaxFF simulations involving external fields.
18
+ """
19
+
20
+
21
+ import argparse
22
+ from pathlib import Path
23
+ from reaxkit.utils.units import unit_for
24
+ from reaxkit.io.handlers.fort78_handler import Fort78Handler
25
+ from reaxkit.analysis.per_file.fort78_analyzer import get_fort78_data
26
+ from reaxkit.utils.media.plotter import single_plot
27
+ from reaxkit.utils.media.convert import convert_xaxis
28
+ from reaxkit.utils.path import resolve_output_path
29
+
30
+ def _export_csv(x, x_label: str, y, y_label: str, path: str) -> None:
31
+ import pandas as pd
32
+ df = pd.DataFrame({x_label: x, y_label: y})
33
+ out = Path(path)
34
+ out.parent.mkdir(parents=True, exist_ok=True)
35
+ df.to_csv(out, index=False)
36
+ print(f"[Done] exported data to {out}")
37
+
38
+
39
+ def _get_task(args: argparse.Namespace) -> int:
40
+ """
41
+ Get/plot/export a single yaxis from fort.78 vs iter/frame/time.
42
+ """
43
+ handler = Fort78Handler(args.file)
44
+
45
+ # Pull requested yaxis from fort.78 summary.
46
+ # Analyzer will *rename* the output yaxis to the *requested alias*.
47
+ ykey: str = args.yaxis.strip()
48
+ df = get_fort78_data(handler, [ykey]) # DataFrame: columns ['iter', ykey]
49
+
50
+ if ykey not in df.columns:
51
+ raise KeyError(f"❌ yaxis '{ykey}' not found in fort.78 data.")
52
+
53
+ # Select and sanitize x/y
54
+ import pandas as pd
55
+ iters = pd.to_numeric(df["iter"], errors="coerce")
56
+ iters = iters.dropna().to_numpy()
57
+
58
+ xvals, xlabel = convert_xaxis(iters, args.xaxis, control_file=args.control)
59
+
60
+ yvals = pd.to_numeric(df[ykey], errors="coerce").to_numpy()
61
+
62
+ workflow_name = args.kind
63
+ # Export CSV if requested
64
+ if args.export:
65
+ out = resolve_output_path(args.export, workflow_name)
66
+ _export_csv(xvals, xlabel, yvals, ykey, out)
67
+
68
+ # Plot and/or save plot
69
+ if args.plot or args.save:
70
+ # Ensure plain numeric arrays before plotting
71
+ import numpy as np
72
+ x_plot = np.asarray(xvals, dtype=float)
73
+ y_plot = np.asarray(yvals, dtype=float)
74
+
75
+ u = unit_for(args.yaxis) or unit_for(ykey)
76
+ if args.save:
77
+ out = resolve_output_path(args.save, workflow_name)
78
+ single_plot(
79
+ x=x_plot,
80
+ y=y_plot,
81
+ title=f"{ykey} vs {xlabel}",
82
+ xlabel=xlabel,
83
+ ylabel=f"{ykey} ({u})" if u else ykey,
84
+ save=out,
85
+ legend=False,
86
+ )
87
+ elif args.plot:
88
+ single_plot(
89
+ x=x_plot,
90
+ y=y_plot,
91
+ title=f"{ykey} vs {xlabel}",
92
+ xlabel=xlabel,
93
+ ylabel=f"{ykey} ({u})" if u else ykey,
94
+ legend=False,
95
+ )
96
+
97
+ # If neither plot nor export was requested, print a short tip
98
+ if not (args.plot or args.save or args.export or args.head):
99
+ print("ℹ️ Nothing plotted or exported. Use --plot, --save <path>, --export <csv>, or --head N to preview rows.")
100
+
101
+ return 0
102
+
103
+
104
+ def register_tasks(subparsers: argparse._SubParsersAction) -> None:
105
+ """
106
+ Register only one task under the already-created 'fort78' workflow:
107
+ reaxkit fort78 get ...
108
+ """
109
+ g = subparsers.add_parser(
110
+ "get",
111
+ help="Get/plot/export a single yaxis from fort.78 vs iter/frame/time.\n",
112
+ description=(
113
+ "Examples:\n"
114
+ " reaxkit fort78 get --xaxis time --yaxis E_field_x --save E_field_x.png --export E_field_x.csv\n"
115
+ ),
116
+ formatter_class=argparse.RawTextHelpFormatter,
117
+ )
118
+ g.add_argument("--file", default="fort.78", help="Path to fort.78 file")
119
+ g.add_argument("--yaxis",
120
+ required=True,
121
+ help="Name of the fort.78 yaxis to extract (e.g., 'E_field_x')",
122
+ )
123
+ g.add_argument("--xaxis",
124
+ choices=("iter", "frame", "time"), default="iter",
125
+ help="X-axis for plotting/export (default: iter). 'time' may require a control file.",
126
+ )
127
+ g.add_argument("--control", default="control",
128
+ help="Path to control file (only used when --xaxis time).",
129
+ )
130
+ # Output options
131
+ g.add_argument("--plot", action="store_true", help="Show a plot in a window")
132
+ g.add_argument("--save", default=None, help="Save plot to a path or directory")
133
+ g.add_argument("--export", default=None, help="Export data to CSV at this path")
134
+
135
+ g.set_defaults(_run=_get_task)