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
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()
|
reaxkit/data/alias.yaml
ADDED
|
@@ -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
|
+
|