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
reaxkit/cli.py ADDED
@@ -0,0 +1,181 @@
1
+ """
2
+ Top-level command-line interface for ReaxKit workflows.
3
+
4
+ Works on: ReaxKit CLI (`reaxkit`) → dispatches to workflow modules under `reaxkit.workflows`.
5
+
6
+ This module defines the `reaxkit` entry point and routes each top-level subcommand
7
+ ("kind") to its corresponding workflow module, which then registers task-level
8
+ subcommands (or runs as a kind-level workflow such as `help` / `intspec`).
9
+ """
10
+
11
+ import argparse
12
+ import sys
13
+
14
+ from reaxkit.workflows.meta import make_video_workflow, plotter_workflow, help_workflow, introspection_workflow
15
+ from reaxkit.workflows.per_file import fort57_workflow, summary_workflow, geo_workflow, trainset_workflow, \
16
+ fort76_workflow, fort78_workflow, xmolout_workflow, fort83_workflow, eregime_workflow, fort79_workflow, \
17
+ fort99_workflow, control_workflow, ffield_workflow, vels_workflow, fort13_workflow, tregime_workflow, \
18
+ params_workflow, fort7_workflow, vregime_workflow, molfra_workflow, fort73_workflow, fort74_workflow
19
+ from reaxkit.workflows.composed import coordination_workflow, xmolout_fort7_workflow, electrostatics_workflow
20
+
21
+ # Mapping from top-level CLI "kind" (subcommand) to the workflow module
22
+ # that knows how to register its own tasks and arguments.
23
+
24
+ # important note: energylog and fort.58 have exactly the same structure as fort.73, hence they should map to fort.73
25
+ # also, fort.8 is similar to fort.7 in the same way
26
+ # same for molsav and moldyn which are similar to vels
27
+ WORKFLOW_MODULES = {
28
+ "fort78": fort78_workflow, "xmolout": xmolout_workflow, "summary": summary_workflow,
29
+ "eregime": eregime_workflow, "molfra": molfra_workflow, "fort13": fort13_workflow,
30
+ "fort79": fort79_workflow, "fort7": fort7_workflow, "xmolfort7": xmolout_fort7_workflow,
31
+ "coord": coordination_workflow, "intspec": introspection_workflow, "geo": geo_workflow,
32
+ "fort99": fort99_workflow, "trainset": trainset_workflow, "fort83": fort83_workflow,
33
+ "fort73": fort73_workflow, "elect": electrostatics_workflow, "video": make_video_workflow,
34
+ "plotter": plotter_workflow, "control": control_workflow, "fort76": fort76_workflow,
35
+ "fort74.md": fort74_workflow, "ffield": ffield_workflow, "params": params_workflow,
36
+ "energylog": fort73_workflow, "fort58": fort73_workflow, "fort57.md": fort57_workflow,
37
+ "vels": vels_workflow, "help": help_workflow, "fort8": fort7_workflow,
38
+ "moldyn": vels_workflow, "molsav": vels_workflow, "tregime": tregime_workflow,
39
+ "vregime": vregime_workflow,
40
+ }
41
+
42
+
43
+ # Workflows that are allowed to omit an explicit task; a default task
44
+ # will be injected for them if the user does not provide one.
45
+ DEFAULTABLE = {}
46
+
47
+ # Names that are treated as "known tasks" when deciding whether to inject
48
+ # a synthetic `_default` task for DEFAULTABLE workflows.
49
+ DEFAULT_TASKS = {}
50
+
51
+
52
+ def _preinject(argv):
53
+ """
54
+ Inject a default task for selected workflows before argparse parsing.
55
+
56
+ Works on: CLI argv preprocessing for workflows that allow omitting an explicit task.
57
+
58
+ Parameters
59
+ ----------
60
+ argv : Sequence[str]
61
+ Raw argument vector (typically `sys.argv`).
62
+
63
+ Returns
64
+ -------
65
+ list[str]
66
+ A rewritten argv where a synthetic `_default` task is inserted for
67
+ workflows in `DEFAULTABLE` when the next token is not a known task.
68
+
69
+ Examples
70
+ --------
71
+ >>> _preinject(["reaxkit", "gplot", "file.txt"])
72
+ ['reaxkit', 'gplot', '_default', 'file.txt']
73
+ """
74
+ out = list(argv)
75
+ for i, tok in enumerate(out):
76
+ if tok in DEFAULTABLE:
77
+ j = i + 1
78
+ if j >= len(out):
79
+ break
80
+ nxt = out[j]
81
+ if nxt in ("-h", "--help"):
82
+ break # allow `reaxkit gplot -h` to show help normally
83
+ if nxt not in DEFAULT_TASKS: # filename or option → inject default task
84
+ out.insert(j, "_default")
85
+ break
86
+ return out
87
+
88
+
89
+ def _intspec_default_runner(args):
90
+ """
91
+ Run the `intspec` workflow without requiring a task subcommand.
92
+
93
+ Works on: `intspec` kind-level workflow (introspection).
94
+
95
+ Parameters
96
+ ----------
97
+ args : argparse.Namespace
98
+ Parsed CLI arguments; may include `file` and/or `folder`.
99
+
100
+ Returns
101
+ -------
102
+ int
103
+ Workflow exit code returned by `introspection_workflow.run_main(...)`.
104
+
105
+ Examples
106
+ --------
107
+ # Equivalent CLI calls:
108
+ # reaxkit intspec --file fort7_analyzer
109
+ # reaxkit intspec --folder workflow
110
+ """
111
+ return introspection_workflow.run_main(
112
+ getattr(args, "file", None),
113
+ getattr(args, "folder", None),
114
+ )
115
+
116
+
117
+ def main():
118
+ """
119
+ Build and execute the `reaxkit` CLI dispatcher.
120
+
121
+ Works on: ReaxKit CLI (`reaxkit`) and registered workflow modules.
122
+
123
+ This function:
124
+ 1. Preprocesses argv to inject default tasks where needed.
125
+ 2. Creates the top-level parser and the `kind` subparsers.
126
+ 3. Lets each workflow module register its own `task` subparsers.
127
+ 4. Parses the CLI and dispatches to the selected task's `_run` function.
128
+
129
+ Returns
130
+ -------
131
+ int
132
+ Exit code returned by the selected workflow task runner.
133
+
134
+ Examples
135
+ --------
136
+ Per-file workflow with task:
137
+
138
+ - reaxkit fort7 get --file fort.7
139
+
140
+ Kind-level workflow without task:
141
+
142
+ - reaxkit help --query "fort.7"
143
+
144
+ - reaxkit intspec --folder workflows
145
+ """
146
+ # Preprocess argv so DEFAULTABLE workflows can omit an explicit task.
147
+ sys_argv = _preinject(sys.argv)
148
+
149
+ # Top-level parser for `reaxkit`
150
+ parser = argparse.ArgumentParser("reaxkit CLI")
151
+
152
+ # First level of subcommands: the workflow "kind" (summary, xmolout, etc.)
153
+ sub = parser.add_subparsers(dest="kind", required=True)
154
+
155
+ # For each workflow module, create its own subparser and let it
156
+ # register its internal tasks (second-level subcommands).
157
+ for kind, module in WORKFLOW_MODULES.items():
158
+ # e.g. `reaxkit summary ...`, `reaxkit xmolout ...`
159
+ kp = sub.add_parser(kind, help=f"{kind} workflows")
160
+
161
+ if kind == "intspec":
162
+ # Kind-level workflow: no subcommands
163
+ if hasattr(module, "build_parser"):
164
+ module.build_parser(kp)
165
+ kp.set_defaults(_run=_intspec_default_runner)
166
+
167
+ elif kind == "help":
168
+ # Kind-level workflow: no subcommands
169
+ if hasattr(module, "build_parser"):
170
+ module.build_parser(kp)
171
+ kp.set_defaults(_run=module.run_main)
172
+
173
+ else:
174
+ # Normal workflows: require a task unless the kind is in DEFAULTABLE.
175
+ # e.g. `reaxkit summary get ...`
176
+ tasks = kp.add_subparsers(dest="task", required=kind not in DEFAULTABLE)
177
+ module.register_tasks(tasks)
178
+
179
+ # Parse the CLI (minus the program name) and dispatch to the chosen task.
180
+ args = parser.parse_args(sys_argv[1:])
181
+ return args._run(args)
reaxkit/count_loc.py ADDED
@@ -0,0 +1,276 @@
1
+ """Count lines of code in a Python project.
2
+
3
+ Usage (from inside your project directory):
4
+ python count_loc.py
5
+ Optional:
6
+ python count_loc.py --root . --out loc_report.csv
7
+ """
8
+
9
+ from __future__ import annotations
10
+ import argparse
11
+ import ast
12
+ import csv
13
+ from pathlib import Path
14
+
15
+ # Folders to skip during the walk
16
+ DEFAULT_EXCLUDES = {
17
+ ".git",
18
+ ".hg",
19
+ ".svn",
20
+ ".mypy_cache",
21
+ ".pytest_cache",
22
+ "__pycache__",
23
+ ".venv",
24
+ "venv",
25
+ "env",
26
+ "build",
27
+ "dist",
28
+ ".idea",
29
+ ".vscode",
30
+ "site-packages",
31
+ "node_modules",
32
+ }
33
+
34
+
35
+ def is_comment_or_blank(line: str) -> bool:
36
+ """
37
+ Legacy helper: returns True if the line is blank or a comment-only line.
38
+ Kept for backward compatibility but not used in main logic.
39
+ """
40
+ s = line.strip()
41
+ return (not s) or s.startswith("#")
42
+
43
+
44
+ def iter_python_files(root: Path, excludes: set[str]) -> list[Path]:
45
+ """Recursively find all .py files under root, skipping excluded dirs."""
46
+ files = []
47
+ for p in root.rglob("*.py"):
48
+ # Skip excluded directories anywhere in the path
49
+ if any(part in excludes for part in p.parts):
50
+ continue
51
+ files.append(p)
52
+ return files
53
+
54
+
55
+ def _docstring_line_numbers(source: str, filename: str) -> set[int]:
56
+ """
57
+ Return a set of line numbers (1-based) that belong to *docstrings*
58
+ (module, class, function/async function) based on the AST.
59
+ """
60
+ doc_lines: set[int] = set()
61
+ try:
62
+ tree = ast.parse(source, filename=filename)
63
+ except SyntaxError:
64
+ # If the file is not valid Python (or incomplete), treat everything
65
+ # as non-docstring.
66
+ return doc_lines
67
+
68
+ # Module-level and all function/class nodes
69
+ targets = [tree]
70
+ targets.extend(
71
+ node
72
+ for node in ast.walk(tree)
73
+ if isinstance(
74
+ node,
75
+ (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef),
76
+ )
77
+ )
78
+
79
+ for node in targets:
80
+ # ast.get_docstring tells us whether a docstring exists
81
+ if ast.get_docstring(node, clean=False) is None:
82
+ continue
83
+
84
+ # Docstring must be the first statement in the body
85
+ body = getattr(node, "body", [])
86
+ if not body:
87
+ continue
88
+ expr0 = body[0]
89
+ if not isinstance(expr0, ast.Expr):
90
+ continue
91
+
92
+ value = expr0.value
93
+ # Py3.8+: docstring is usually ast.Constant; older: ast.Str
94
+ lineno = getattr(value, "lineno", None)
95
+ end_lineno = getattr(value, "end_lineno", None) or lineno
96
+ if lineno is None:
97
+ continue
98
+
99
+ for ln in range(lineno, end_lineno + 1):
100
+ doc_lines.add(ln)
101
+
102
+ return doc_lines
103
+
104
+
105
+ def classify_python_file(py_path: Path) -> dict[str, int]:
106
+ """
107
+ Classify each line in a .py file as code, comment, docstring, or blank.
108
+
109
+ Returns a dict with keys:
110
+ - code
111
+ - comments
112
+ - docstrings
113
+ - blank
114
+ - total
115
+ """
116
+ try:
117
+ text = py_path.read_text(encoding="utf-8", errors="ignore")
118
+ except Exception:
119
+ # If unreadable, treat as 0 for all categories.
120
+ return {"code": 0, "comments": 0, "docstrings": 0, "blank": 0, "total": 0}
121
+
122
+ lines = text.splitlines()
123
+ docstring_lines = _docstring_line_numbers(text, str(py_path))
124
+
125
+ code = comments = docstrings = blank = 0
126
+
127
+ for lineno, line in enumerate(lines, start=1):
128
+ stripped = line.strip()
129
+
130
+ if not stripped:
131
+ blank += 1
132
+ elif lineno in docstring_lines:
133
+ # Mark docstring lines first to avoid misclassifying them
134
+ # as comments or code.
135
+ docstrings += 1
136
+ elif stripped.startswith("#"):
137
+ # Comment-only line
138
+ comments += 1
139
+ else:
140
+ # Everything else is treated as code (includes regular strings,
141
+ # inline comments, etc.).
142
+ code += 1
143
+
144
+ total = code + comments + docstrings + blank
145
+ return {
146
+ "code": code,
147
+ "comments": comments,
148
+ "docstrings": docstrings,
149
+ "blank": blank,
150
+ "total": total,
151
+ }
152
+
153
+
154
+ def main() -> None:
155
+ parser = argparse.ArgumentParser(
156
+ description=(
157
+ "Recursively count Python LOC and export CSV with per-file breakdown "
158
+ "(code, comments, docstrings, blank)."
159
+ )
160
+ )
161
+ parser.add_argument(
162
+ "--root",
163
+ type=str,
164
+ default=".",
165
+ help="Root directory to scan (default: current directory).",
166
+ )
167
+ parser.add_argument(
168
+ "--out",
169
+ type=str,
170
+ default="loc_report.csv",
171
+ help="Output CSV filename (default: loc_report.csv).",
172
+ )
173
+ parser.add_argument(
174
+ "--include-comments",
175
+ action="store_true",
176
+ help=(
177
+ "Count all physical lines as LOC metric (code + comments + docstrings + blank). "
178
+ "By default, only code lines are used for the LOC metric."
179
+ ),
180
+ )
181
+ parser.add_argument(
182
+ "--extra-exclude",
183
+ action="append",
184
+ default=[],
185
+ help="Extra folder name(s) to exclude (can be used multiple times).",
186
+ )
187
+
188
+ args = parser.parse_args()
189
+ root = Path(args.root).resolve()
190
+ out_csv = Path(args.out).resolve()
191
+ excludes = set(DEFAULT_EXCLUDES) | set(args.extra_exclude)
192
+
193
+ if not root.exists():
194
+ raise SystemExit(f"Root path not found: {root}")
195
+
196
+ py_files = iter_python_files(root, excludes)
197
+ py_files.sort()
198
+
199
+ csv_rows = []
200
+ total_loc_metric = 0 # what we print as "Total lines of code" or "Total counted lines"
201
+
202
+ # Global totals for summary
203
+ global_counts = {"code": 0, "comments": 0, "docstrings": 0, "blank": 0, "total": 0}
204
+
205
+ # Index to sort by: code (1) or total (5)
206
+ sort_index = 5 if args.include_comments else 1
207
+
208
+ for f in py_files:
209
+ counts = classify_python_file(f)
210
+
211
+ # Update global totals
212
+ for key in global_counts:
213
+ global_counts[key] += counts[key]
214
+
215
+ # Metric used for totals & ranking
216
+ loc_metric = counts["total"] if args.include_comments else counts["code"]
217
+ total_loc_metric += loc_metric
218
+
219
+ rel = f.relative_to(root)
220
+ csv_rows.append(
221
+ (
222
+ str(rel),
223
+ counts["code"],
224
+ counts["comments"],
225
+ counts["docstrings"],
226
+ counts["blank"],
227
+ counts["total"],
228
+ )
229
+ )
230
+
231
+ # Sort CSV rows by chosen metric (code or total)
232
+ csv_rows.sort(key=lambda row: row[sort_index], reverse=True)
233
+
234
+ # Write CSV
235
+ out_csv.parent.mkdir(parents=True, exist_ok=True)
236
+ with out_csv.open("w", newline="", encoding="utf-8") as fh:
237
+ writer = csv.writer(fh)
238
+ writer.writerow(["file", "code", "comments", "docstrings", "blank", "total"])
239
+ writer.writerows(csv_rows)
240
+
241
+ # Print metric summary
242
+ metric_label = (
243
+ "Total counted lines (code + comments + docstrings + blank)"
244
+ if args.include_comments
245
+ else "Total lines of code (excluding comments/docstrings/blanks)"
246
+ )
247
+
248
+ print(f"\nWrote {len(csv_rows)} files to: {out_csv}")
249
+ print(f"{metric_label}: {total_loc_metric:,}\n")
250
+
251
+ # Print top 6 files
252
+ print("Top 6 files by this metric:")
253
+ for file, code, comments, docs, blank, total in csv_rows[:6]:
254
+ loc_metric = total if args.include_comments else code
255
+ print(f" {file:<40} {loc_metric:,}")
256
+ print()
257
+
258
+ # --- Global summary by line type with percentages ---
259
+ total_lines = global_counts["total"] or 1 # avoid division by zero
260
+
261
+ def fmt(name: str, count: int) -> str:
262
+ pct = (count / total_lines) * 100.0
263
+ return f"{name:<25} {count:>8,} ({pct:>3.0f}%)"
264
+
265
+ print("Summary by Line Type:")
266
+ print(fmt("Total lines of code:", global_counts["code"]))
267
+ print(fmt("Total comment lines:", global_counts["comments"]))
268
+ print(fmt("Total docstring lines:", global_counts["docstrings"]))
269
+ print(fmt("Total blank lines:", global_counts["blank"]))
270
+ print("-" * 50)
271
+ print(f"{'Total physical lines:':<25} {global_counts['total']:>8,}")
272
+ print()
273
+
274
+
275
+ if __name__ == "__main__":
276
+ main()
@@ -0,0 +1,89 @@
1
+ # Canonical-to-alias mappings for resolving equivalent column and variable names across ReaxFF files.
2
+ # In the CLI, aliases allow users to reference variables using familiar or abbreviated names.
3
+ # In the Python API, canonical names are used internally to ensure consistency and predictability.
4
+
5
+ aliases:
6
+ iter:
7
+ - iteration
8
+ - Iter
9
+ - Iter.
10
+ - Iteration
11
+ - "#start"
12
+ - start
13
+
14
+ E_pot:
15
+ - Epot(kcal)
16
+ - Epot(kcal/mol)
17
+ - E_potential
18
+
19
+ frame: [frm]
20
+ time: ["Time(fs)", "Time"]
21
+ num_of_atoms: [num_atoms, number_of_atoms, count_of_atoms]
22
+ V: ["Vol(A^3)", "Volume", "volume"]
23
+ D: ["Dens(kg/dm3)", "Density", "density"]
24
+
25
+ # molfra.out
26
+ freq: [frequency, count]
27
+ molecular_formula: [mol_formula, molecule_formula, molecule]
28
+ molecular_mass: [mol_mass, mass]
29
+ total_molecules: [tot_mol]
30
+ total_atoms: [tot_atom]
31
+ total_molecular_mass: [tot_mol_mass]
32
+
33
+ # fort.78
34
+ field_x: [Ex, Efield_x, Field_x, fieldX]
35
+ field_y: [Ey, Efield_y, Field_y, fieldY]
36
+ field_z: [Ez, Efield_z, Field_z, fieldZ]
37
+ E_field_x: [Efx]
38
+ E_field_y: [Efy]
39
+ E_field_z: [Efz]
40
+ E_field: [Ef]
41
+
42
+ # summary.txt
43
+ nmol: [Nmol]
44
+ T: ["T(K)", Temp, temp]
45
+ P: ["Pres(MPa)", Pressure, pressure]
46
+ elap_time: [Elap, time_elapsed, elapsed_time]
47
+
48
+ # fort.13
49
+ total_ff_error: [tot_err, err_tot]
50
+
51
+ # eregime.in
52
+ field_zones: ["#V", V]
53
+ field_dir: [direction, dir, direction1]
54
+ field: ["E", "Magnitude(V/A)", "Magnitude1(V/A)", E1]
55
+ field_dir1: [direction1]
56
+ field1: ["Magnitude1(V/A)", E1]
57
+ field_dir2: [direction2]
58
+ field2: ["Magnitude2(V/A)", E2]
59
+ field_dir3: [direction3]
60
+ field3: ["Magnitude3(V/A)", E3]
61
+
62
+ # xmolout
63
+ atom_type: [type_of_atom, atm_type]
64
+ x: [x_coordinate, x_coord, coord_x, coordinate_x]
65
+ y: [y_coordinate, y_coord, coord_y, coordinate_y]
66
+ z: [z_coordinate, z_coord, coord_z, coordinate_z]
67
+
68
+ # fort.7
69
+ molecule_num: [molecular_number, molecular_num]
70
+ partial_charge: [charge, q, total_charge]
71
+
72
+ # fort.99
73
+ error: [Err, Error]
74
+
75
+ # electrostatics
76
+ "mu_x (debye)": [mu_x, dipole_x, dipole_moment_x]
77
+ "mu_y (debye)": [mu_y, dipole_y, dipole_moment_y]
78
+ "mu_z (debye)": [mu_z, dipole_z, dipole_moment_z]
79
+ "P_x (uC/cm^2)": [pol_x, polarization_x]
80
+ "P_y (uC/cm^2)": [pol_y, polarization_y]
81
+ "P_z (uC/cm^2)": [pol_z, polarization_z]
82
+
83
+ # fort.76
84
+ E_res: [Eres(kcal), Eres(kcal/mol), E_restraint, restraint_energy, restraint_E]
85
+
86
+ # fort.57
87
+ T_set: [Tset, "Tset(K)", "Tset (K)", "T_set(K)"]
88
+ RMSG: [rmsg, RmsG, RMSg]
89
+ nfc: [NFC]
@@ -0,0 +1,27 @@
1
+ # Physical constants and fixed conversion factors used across ReaxKit
2
+
3
+ constants:
4
+ # Electric field
5
+ electric_field_VA_to_MVcm: 100.0 # V/Å → MV/cm
6
+
7
+ # Energies
8
+ energy_kcalmol_to_eV: 0.0433634
9
+
10
+ # Fundamental constants
11
+ electron_charge_C: 1.602176634e-19 # Coulomb
12
+ electron_charge_e: 1.0 # dimensionless charge
13
+
14
+ # Dipole moment
15
+ ea_to_debye: 4.80320427 # e·Å → Debye
16
+ debye_to_ea: 0.20819434 # Debye → e·Å
17
+
18
+ # Polarization (dipole / volume)
19
+ ea3_to_uC_cm2: 1602.176634 # (e·Å)/ų → μC/cm²
20
+
21
+ # Pressure
22
+ eV_per_A3_to_GPa: 160.21766208 # eV/ų → GPa
23
+
24
+ # AVOGADRO_CONSTANT
25
+ AVOGADRO_CONSTANT: 6.022140857
26
+
27
+