reaxkit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reaxkit/__init__.py +0 -0
- reaxkit/analysis/__init__.py +0 -0
- reaxkit/analysis/composed/RDF_analyzer.py +560 -0
- reaxkit/analysis/composed/__init__.py +0 -0
- reaxkit/analysis/composed/connectivity_analyzer.py +706 -0
- reaxkit/analysis/composed/coordination_analyzer.py +144 -0
- reaxkit/analysis/composed/electrostatics_analyzer.py +687 -0
- reaxkit/analysis/per_file/__init__.py +0 -0
- reaxkit/analysis/per_file/control_analyzer.py +165 -0
- reaxkit/analysis/per_file/eregime_analyzer.py +108 -0
- reaxkit/analysis/per_file/ffield_analyzer.py +305 -0
- reaxkit/analysis/per_file/fort13_analyzer.py +79 -0
- reaxkit/analysis/per_file/fort57_analyzer.py +106 -0
- reaxkit/analysis/per_file/fort73_analyzer.py +61 -0
- reaxkit/analysis/per_file/fort74_analyzer.py +65 -0
- reaxkit/analysis/per_file/fort76_analyzer.py +191 -0
- reaxkit/analysis/per_file/fort78_analyzer.py +154 -0
- reaxkit/analysis/per_file/fort79_analyzer.py +83 -0
- reaxkit/analysis/per_file/fort7_analyzer.py +393 -0
- reaxkit/analysis/per_file/fort99_analyzer.py +411 -0
- reaxkit/analysis/per_file/molfra_analyzer.py +359 -0
- reaxkit/analysis/per_file/params_analyzer.py +258 -0
- reaxkit/analysis/per_file/summary_analyzer.py +84 -0
- reaxkit/analysis/per_file/trainset_analyzer.py +84 -0
- reaxkit/analysis/per_file/vels_analyzer.py +95 -0
- reaxkit/analysis/per_file/xmolout_analyzer.py +528 -0
- reaxkit/cli.py +181 -0
- reaxkit/count_loc.py +276 -0
- reaxkit/data/alias.yaml +89 -0
- reaxkit/data/constants.yaml +27 -0
- reaxkit/data/reaxff_input_files_contents.yaml +186 -0
- reaxkit/data/reaxff_output_files_contents.yaml +301 -0
- reaxkit/data/units.yaml +38 -0
- reaxkit/help/__init__.py +0 -0
- reaxkit/help/help_index_loader.py +531 -0
- reaxkit/help/introspection_utils.py +131 -0
- reaxkit/io/__init__.py +0 -0
- reaxkit/io/base_handler.py +165 -0
- reaxkit/io/generators/__init__.py +0 -0
- reaxkit/io/generators/control_generator.py +123 -0
- reaxkit/io/generators/eregime_generator.py +341 -0
- reaxkit/io/generators/geo_generator.py +967 -0
- reaxkit/io/generators/trainset_generator.py +1758 -0
- reaxkit/io/generators/tregime_generator.py +113 -0
- reaxkit/io/generators/vregime_generator.py +164 -0
- reaxkit/io/generators/xmolout_generator.py +304 -0
- reaxkit/io/handlers/__init__.py +0 -0
- reaxkit/io/handlers/control_handler.py +209 -0
- reaxkit/io/handlers/eregime_handler.py +122 -0
- reaxkit/io/handlers/ffield_handler.py +812 -0
- reaxkit/io/handlers/fort13_handler.py +123 -0
- reaxkit/io/handlers/fort57_handler.py +143 -0
- reaxkit/io/handlers/fort73_handler.py +145 -0
- reaxkit/io/handlers/fort74_handler.py +155 -0
- reaxkit/io/handlers/fort76_handler.py +195 -0
- reaxkit/io/handlers/fort78_handler.py +142 -0
- reaxkit/io/handlers/fort79_handler.py +227 -0
- reaxkit/io/handlers/fort7_handler.py +264 -0
- reaxkit/io/handlers/fort99_handler.py +128 -0
- reaxkit/io/handlers/geo_handler.py +224 -0
- reaxkit/io/handlers/molfra_handler.py +184 -0
- reaxkit/io/handlers/params_handler.py +137 -0
- reaxkit/io/handlers/summary_handler.py +135 -0
- reaxkit/io/handlers/trainset_handler.py +658 -0
- reaxkit/io/handlers/vels_handler.py +293 -0
- reaxkit/io/handlers/xmolout_handler.py +174 -0
- reaxkit/utils/__init__.py +0 -0
- reaxkit/utils/alias.py +219 -0
- reaxkit/utils/cache.py +77 -0
- reaxkit/utils/constants.py +75 -0
- reaxkit/utils/equation_of_states.py +96 -0
- reaxkit/utils/exceptions.py +27 -0
- reaxkit/utils/frame_utils.py +175 -0
- reaxkit/utils/log.py +43 -0
- reaxkit/utils/media/__init__.py +0 -0
- reaxkit/utils/media/convert.py +90 -0
- reaxkit/utils/media/make_video.py +91 -0
- reaxkit/utils/media/plotter.py +812 -0
- reaxkit/utils/numerical/__init__.py +0 -0
- reaxkit/utils/numerical/extrema_finder.py +96 -0
- reaxkit/utils/numerical/moving_average.py +103 -0
- reaxkit/utils/numerical/numerical_calcs.py +75 -0
- reaxkit/utils/numerical/signal_ops.py +135 -0
- reaxkit/utils/path.py +55 -0
- reaxkit/utils/units.py +104 -0
- reaxkit/webui/__init__.py +0 -0
- reaxkit/webui/app.py +0 -0
- reaxkit/webui/components.py +0 -0
- reaxkit/webui/layouts.py +0 -0
- reaxkit/webui/utils.py +0 -0
- reaxkit/workflows/__init__.py +0 -0
- reaxkit/workflows/composed/__init__.py +0 -0
- reaxkit/workflows/composed/coordination_workflow.py +393 -0
- reaxkit/workflows/composed/electrostatics_workflow.py +587 -0
- reaxkit/workflows/composed/xmolout_fort7_workflow.py +343 -0
- reaxkit/workflows/meta/__init__.py +0 -0
- reaxkit/workflows/meta/help_workflow.py +136 -0
- reaxkit/workflows/meta/introspection_workflow.py +235 -0
- reaxkit/workflows/meta/make_video_workflow.py +61 -0
- reaxkit/workflows/meta/plotter_workflow.py +601 -0
- reaxkit/workflows/per_file/__init__.py +0 -0
- reaxkit/workflows/per_file/control_workflow.py +110 -0
- reaxkit/workflows/per_file/eregime_workflow.py +267 -0
- reaxkit/workflows/per_file/ffield_workflow.py +390 -0
- reaxkit/workflows/per_file/fort13_workflow.py +86 -0
- reaxkit/workflows/per_file/fort57_workflow.py +137 -0
- reaxkit/workflows/per_file/fort73_workflow.py +151 -0
- reaxkit/workflows/per_file/fort74_workflow.py +88 -0
- reaxkit/workflows/per_file/fort76_workflow.py +188 -0
- reaxkit/workflows/per_file/fort78_workflow.py +135 -0
- reaxkit/workflows/per_file/fort79_workflow.py +314 -0
- reaxkit/workflows/per_file/fort7_workflow.py +592 -0
- reaxkit/workflows/per_file/fort83_workflow.py +60 -0
- reaxkit/workflows/per_file/fort99_workflow.py +223 -0
- reaxkit/workflows/per_file/geo_workflow.py +554 -0
- reaxkit/workflows/per_file/molfra_workflow.py +577 -0
- reaxkit/workflows/per_file/params_workflow.py +135 -0
- reaxkit/workflows/per_file/summary_workflow.py +161 -0
- reaxkit/workflows/per_file/trainset_workflow.py +356 -0
- reaxkit/workflows/per_file/tregime_workflow.py +79 -0
- reaxkit/workflows/per_file/vels_workflow.py +309 -0
- reaxkit/workflows/per_file/vregime_workflow.py +75 -0
- reaxkit/workflows/per_file/xmolout_workflow.py +678 -0
- reaxkit-1.0.0.dist-info/METADATA +128 -0
- reaxkit-1.0.0.dist-info/RECORD +130 -0
- reaxkit-1.0.0.dist-info/WHEEL +5 -0
- reaxkit-1.0.0.dist-info/entry_points.txt +2 -0
- reaxkit-1.0.0.dist-info/licenses/AUTHORS.md +20 -0
- reaxkit-1.0.0.dist-info/licenses/LICENSE +21 -0
- reaxkit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base file-handler abstraction for ReaxKit.
|
|
3
|
+
|
|
4
|
+
This module defines the abstract ``FileHandler`` class, which provides the
|
|
5
|
+
common interface and lifecycle used by all ReaxKit file handlers
|
|
6
|
+
(e.g., ``XmoloutHandler``, ``Fort7Handler``, ``SummaryHandler``).
|
|
7
|
+
|
|
8
|
+
The base class standardizes how ReaxFF output files are:
|
|
9
|
+
|
|
10
|
+
- loaded from disk
|
|
11
|
+
- parsed lazily into structured tabular data
|
|
12
|
+
- exposed via a uniform DataFrame-based API
|
|
13
|
+
- accompanied by lightweight metadata
|
|
14
|
+
|
|
15
|
+
All ReaxKit analysis functions rely on ``FileHandler`` subclasses to provide
|
|
16
|
+
a consistent, predictable view of parsed ReaxFF files.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
from abc import ABC, abstractmethod
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any
|
|
24
|
+
import pandas as pd
|
|
25
|
+
|
|
26
|
+
class BaseHandler(ABC):
|
|
27
|
+
"""
|
|
28
|
+
Abstract base class for ReaxKit file handlers.
|
|
29
|
+
|
|
30
|
+
This class defines the minimal public interface that all ReaxKit
|
|
31
|
+
file handlers must implement. Subclasses are responsible for parsing
|
|
32
|
+
a specific ReaxFF file format and exposing its contents as structured
|
|
33
|
+
pandas DataFrames.
|
|
34
|
+
|
|
35
|
+
Parsed Data
|
|
36
|
+
-----------
|
|
37
|
+
Main table
|
|
38
|
+
A pandas.DataFrame returned by ``dataframe()``, whose columns
|
|
39
|
+
depend on the specific file type.
|
|
40
|
+
|
|
41
|
+
Metadata
|
|
42
|
+
A dictionary of lightweight metadata returned by ``metadata()``,
|
|
43
|
+
typically including global or per-file attributes.
|
|
44
|
+
|
|
45
|
+
Notes
|
|
46
|
+
-----
|
|
47
|
+
- Parsing is performed lazily and cached after the first access.
|
|
48
|
+
- Subclasses must implement the private ``_parse()`` method.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, file_path: str | Path):
|
|
52
|
+
"""
|
|
53
|
+
Initialize a file handler with a file path.
|
|
54
|
+
|
|
55
|
+
Works on
|
|
56
|
+
--------
|
|
57
|
+
ReaxFF output files on disk
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
file_path : str or pathlib.Path
|
|
62
|
+
Path to the file to be parsed.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
None
|
|
67
|
+
Initializes the handler without parsing the file.
|
|
68
|
+
"""
|
|
69
|
+
self.path = Path(file_path)
|
|
70
|
+
self._parsed = False
|
|
71
|
+
self._df: pd.DataFrame | None = None
|
|
72
|
+
self._meta: dict[str, Any] = {}
|
|
73
|
+
|
|
74
|
+
# ---- public API
|
|
75
|
+
def parse(self) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Parse the file contents into structured data.
|
|
78
|
+
|
|
79
|
+
Works on
|
|
80
|
+
--------
|
|
81
|
+
FileHandler — ReaxFF output file
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
None
|
|
86
|
+
Parses the file and caches the resulting DataFrame and metadata.
|
|
87
|
+
|
|
88
|
+
Examples
|
|
89
|
+
--------
|
|
90
|
+
>>> h = SomeHandler("file")
|
|
91
|
+
>>> h.parse()
|
|
92
|
+
"""
|
|
93
|
+
if not self._parsed:
|
|
94
|
+
df, meta = self._parse()
|
|
95
|
+
self._df = df
|
|
96
|
+
self._meta = meta or {}
|
|
97
|
+
self._parsed = True
|
|
98
|
+
|
|
99
|
+
def dataframe(self) -> pd.DataFrame:
|
|
100
|
+
"""
|
|
101
|
+
Return the parsed file contents as a pandas DataFrame.
|
|
102
|
+
|
|
103
|
+
Works on
|
|
104
|
+
--------
|
|
105
|
+
FileHandler — ReaxFF output file
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
pandas.DataFrame
|
|
110
|
+
Structured table representing the parsed file contents.
|
|
111
|
+
|
|
112
|
+
Examples
|
|
113
|
+
--------
|
|
114
|
+
>>> h = SomeHandler("file")
|
|
115
|
+
>>> df = h.dataframe()
|
|
116
|
+
"""
|
|
117
|
+
if not self._parsed:
|
|
118
|
+
self.parse()
|
|
119
|
+
assert self._df is not None
|
|
120
|
+
return self._df
|
|
121
|
+
|
|
122
|
+
def metadata(self) -> dict[str, Any]:
|
|
123
|
+
"""
|
|
124
|
+
Return parsed metadata associated with the file.
|
|
125
|
+
|
|
126
|
+
Works on
|
|
127
|
+
--------
|
|
128
|
+
FileHandler — ReaxFF output file
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
dict
|
|
133
|
+
Dictionary of metadata values extracted during parsing.
|
|
134
|
+
|
|
135
|
+
Examples
|
|
136
|
+
--------
|
|
137
|
+
>>> h = SomeHandler("file")
|
|
138
|
+
>>> meta = h.metadata()
|
|
139
|
+
"""
|
|
140
|
+
if not self._parsed:
|
|
141
|
+
self.parse()
|
|
142
|
+
return dict(self._meta)
|
|
143
|
+
|
|
144
|
+
# ---- subclasses must implement
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def _parse(self) -> tuple[pd.DataFrame, dict[str, Any]]:
|
|
147
|
+
"""Parse the file and return structured data and metadata.
|
|
148
|
+
|
|
149
|
+
Works on
|
|
150
|
+
--------
|
|
151
|
+
ReaxFF output files
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
None
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
tuple (pandas.DataFrame, dict)
|
|
160
|
+
Parsed data table and associated metadata.
|
|
161
|
+
|
|
162
|
+
Notes
|
|
163
|
+
-----
|
|
164
|
+
This method must be implemented by all subclasses."""
|
|
165
|
+
...
|
|
File without changes
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ReaxFF control file generation utilities.
|
|
3
|
+
|
|
4
|
+
This module provides deterministic helpers for writing a default ReaxFF
|
|
5
|
+
``control`` input file from a canonical, aligned template.
|
|
6
|
+
|
|
7
|
+
Typical use cases include:
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
- generating a baseline ``control`` file for new run directories
|
|
11
|
+
- ensuring consistent spacing/alignment across generated inputs
|
|
12
|
+
- writing a known-good template for tutorials and examples
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Union
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
CONTROL_TEMPLATE = """# General parameters
|
|
23
|
+
1 iexx
|
|
24
|
+
1 iexy
|
|
25
|
+
1 iexz
|
|
26
|
+
7.5 vlbora
|
|
27
|
+
1 icobo 0: use uncorrected bond orders for mol.nrs. in xmolout, fort.7 and fort.71 1: use corrected bond orders
|
|
28
|
+
0 itrout 1: create diff_traj.xyz-output file with unfolded coordinates
|
|
29
|
+
1 itrans 0: do not back-translate atoms 1: back translate atoms
|
|
30
|
+
1 icentr 0: keep position 1: put centre of mass in centre periodic cell 2: put centre of mass at origin
|
|
31
|
+
0 imetho 0: Normal MD-run 1: Energy minimisation 2:MD-energy minimisation
|
|
32
|
+
1 igeofo 0:xyz-input geometry 1: Biograf input geometry 2: xmol-input geometry
|
|
33
|
+
80.000 axis1 a (for non-periodical systems)
|
|
34
|
+
80.000 axis2 b (for non-periodical systems)
|
|
35
|
+
80.000 axis3 c (for non-periodical systems)
|
|
36
|
+
0.0001 cutof2 BO-cutoff for valency angles and torsion angles
|
|
37
|
+
0.300 cutof3 BO-cutoff for bond order for graphs
|
|
38
|
+
7 icharg Charges. 1:EEM 2:- 3: Shielded EEM 4: Full system EEM 5: Fixed (unit 26) 6: Fragment EEM
|
|
39
|
+
1 ichaen Charges. 1:include charge energy 0: Do not include charge energy
|
|
40
|
+
1 iappen 1: Append fort.7 and fort.8
|
|
41
|
+
0 isurpr 1: Surpress lots of output 2: Read in all geometries at the same time
|
|
42
|
+
25 irecon Frequency of reading control-file
|
|
43
|
+
0 icheck 0: Normal run 1:Check first derivatives;2: Single run
|
|
44
|
+
0 idebug 0: normal run 1: debug run
|
|
45
|
+
3 ixmolo 0: only x,y,z-coordinates in xmolout 1: x,y,z + velocities + molnr. in xmolout 2:x,y,z+mol.nr. 3: x,y,z+mol.nr.+Estrain
|
|
46
|
+
# MD-parameters
|
|
47
|
+
1 imdmet MD-method. 1:NVT/Berendsen thermostat 2:do not use;3:NVE 4: NPT/Berendsen thermo/barostat
|
|
48
|
+
0.250 tstep MD-time step (fs)
|
|
49
|
+
0500.00 mdtemp MD-temperature
|
|
50
|
+
0000.00 tincr Increase/decrease temperature
|
|
51
|
+
2 itdmet 0: T-damp atoms 1: Energy cons 2:System 3: Mols 4: Anderson 5: Mols+2 types of damping
|
|
52
|
+
100.0 tdamp1 1st Berendsen/Anderson temperature damping constant (fs)
|
|
53
|
+
0000.00 mdpres MD-pressure (MPa)
|
|
54
|
+
05000.0 pdamp1 Berendsen pressure damping constant (fs)
|
|
55
|
+
0 inpt 0: Change all cell parameters in NPT-run 1: fixed x 2: fixed y 3: fixed z
|
|
56
|
+
0155000 nmdit Number of MD-iterations
|
|
57
|
+
00001 ichupd Charge update frequency
|
|
58
|
+
025 iout1 Output to unit 71 and unit 73
|
|
59
|
+
100 iout2 Save coordinates
|
|
60
|
+
0 ivels 1:Set vels and accels from moldyn.vel to zero
|
|
61
|
+
00025 itrafr Frequency of trarot-calls
|
|
62
|
+
1 iout3 0: create moldyn.xxxx-files 1: do not create moldyn.xxxx-files
|
|
63
|
+
1 iravel 1: Random initial velocities
|
|
64
|
+
025000 iout6 Save velocity file
|
|
65
|
+
000025 irten Frequency of removal of rotational and translational energy
|
|
66
|
+
0 npreit Nr. of iterations in previous runs
|
|
67
|
+
0.00 range Range for back-translation of atoms
|
|
68
|
+
# MM-parameters
|
|
69
|
+
1.00000 endmm End point criterium for MM energy minimisation
|
|
70
|
+
-00001 imaxmo <0 MD-based energy minimization >0 Steepest descent maximum movement (1/1D6 A) 0: Conjugate gradient
|
|
71
|
+
00000 imaxit Maximum number of iterations
|
|
72
|
+
005 iout4 Frequency of structure output during minimisation
|
|
73
|
+
0 iout5 1:Remove fort.57 and fort.58 files
|
|
74
|
+
1.00250 celopt Cell parameter change
|
|
75
|
+
0 icelo2 Change all cell parameters (0) or only x/y/z axis (1/2/3)
|
|
76
|
+
# FF-optimisation parameters
|
|
77
|
+
0.25 parsca Parameter optimization: parameter step scaling
|
|
78
|
+
0.0100 parext Parameter optimization: extrapolation
|
|
79
|
+
0 icelop 0: No cell parameter optimisation 1:Cell parameter optimisation
|
|
80
|
+
1 igeopt 0: Always use same start gemetries 1:Use latest geometries in optimisation
|
|
81
|
+
0 iincop heat increment optimisation 1: yes 0: no
|
|
82
|
+
25.0000 accerr Accepted increase in error force field
|
|
83
|
+
#Outdated parameters
|
|
84
|
+
0 nreac 0: reactive; 1: non-reactive; 2: Place default atoms
|
|
85
|
+
1 ibiola 0: output *.geo and *.bgf-files 1: surpress *.geo and *.bgf output files
|
|
86
|
+
0 itfix 1:Keep temperature fixed at exactly tset
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def write_control_template(out_path: Union[str, Path] = "control") -> Path:
|
|
91
|
+
"""
|
|
92
|
+
Write the default ReaxFF ``control`` file template to disk.
|
|
93
|
+
|
|
94
|
+
This function writes a fully populated ReaxFF control-file template
|
|
95
|
+
(general, MD, MM, FF-optimization, and outdated sections) to disk,
|
|
96
|
+
preserving the exact spacing and alignment of the canonical template.
|
|
97
|
+
|
|
98
|
+
Works on
|
|
99
|
+
--------
|
|
100
|
+
None — writes a ReaxFF ``control`` input file
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
out_path : str | pathlib.Path, optional
|
|
105
|
+
Output file path to write (default: ``"control"``). Parent
|
|
106
|
+
directories are created automatically if they do not exist.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
pathlib.Path
|
|
111
|
+
The resolved path of the written control file.
|
|
112
|
+
|
|
113
|
+
Examples
|
|
114
|
+
--------
|
|
115
|
+
>>> from reaxkit.io.generators.control_generator import write_control_template
|
|
116
|
+
>>> p = write_control_template("run_001/control")
|
|
117
|
+
>>> p.name
|
|
118
|
+
'control'
|
|
119
|
+
"""
|
|
120
|
+
out_path = Path(out_path)
|
|
121
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
out_path.write_text(CONTROL_TEMPLATE, encoding="utf-8")
|
|
123
|
+
return out_path
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ReaxFF electric-field regime (eregime.in) generators.
|
|
3
|
+
|
|
4
|
+
This module provides deterministic utilities for generating ReaxFF
|
|
5
|
+
``eregime.in`` files, which define time-dependent external electric
|
|
6
|
+
field schedules applied during MD simulations.
|
|
7
|
+
|
|
8
|
+
Generators in this module:
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
- write fully formatted ``eregime.in`` files
|
|
12
|
+
- do not parse simulation output
|
|
13
|
+
- do not run simulations or perform analysis
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Callable, Iterable, Tuple, Union, Sequence
|
|
20
|
+
import math
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"write_a_given_eregime",
|
|
25
|
+
"write_eregime_sinusoidal",
|
|
26
|
+
"write_eregime_smooth_pulse",
|
|
27
|
+
"write_eregime_from_function",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
HEADER_LINES: Sequence[str] = (
|
|
31
|
+
"#Electric field regimes\n",
|
|
32
|
+
"#start #V direction Magnitude(V/A)\n",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _normalize_direction(direction: str) -> str:
|
|
37
|
+
d = direction.strip().lower()
|
|
38
|
+
if d not in {"x", "y", "z"}:
|
|
39
|
+
raise ValueError(f"direction must be one of 'x','y','z'; got {direction!r}")
|
|
40
|
+
return d
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _write_header(f) -> None:
|
|
44
|
+
for line in HEADER_LINES:
|
|
45
|
+
f.write(line)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def write_a_given_eregime(
|
|
49
|
+
file_path: Union[str, Path],
|
|
50
|
+
rows: Iterable[Tuple[int, int, str, float]],
|
|
51
|
+
) -> Path:
|
|
52
|
+
"""
|
|
53
|
+
Write an ``eregime.in`` file from explicit row definitions.
|
|
54
|
+
|
|
55
|
+
Each row defines the electric-field magnitude and direction applied
|
|
56
|
+
at a given iteration.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
file_path : str | Path
|
|
61
|
+
Output path (e.g. ``"eregime.in"``).
|
|
62
|
+
rows : iterable of (iteration, V_index, direction, magnitude)
|
|
63
|
+
Electric-field schedule entries, where:
|
|
64
|
+
- iteration : int
|
|
65
|
+
Simulation iteration number.
|
|
66
|
+
- V_index : int
|
|
67
|
+
Voltage index column expected by ReaxFF.
|
|
68
|
+
- direction : {"x","y","z"}
|
|
69
|
+
Field direction.
|
|
70
|
+
- magnitude : float
|
|
71
|
+
Field magnitude in V/Å.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
Path
|
|
76
|
+
The resolved path of the written ``eregime.in`` file.
|
|
77
|
+
|
|
78
|
+
Examples
|
|
79
|
+
---
|
|
80
|
+
>>> rows = [(0, 1, "z", 0.01), (100, 1, "z", -0.01)]
|
|
81
|
+
>>> write_a_given_eregime("eregime.in", rows)
|
|
82
|
+
PosixPath('eregime.in')
|
|
83
|
+
"""
|
|
84
|
+
file_path = Path(file_path)
|
|
85
|
+
with open(file_path, "w") as f:
|
|
86
|
+
_write_header(f)
|
|
87
|
+
for it, v, d, mag in rows:
|
|
88
|
+
d = _normalize_direction(d)
|
|
89
|
+
f.write(f"{int(it):6d} {int(v):d} {d:<2} {float(mag): .6f}\n")
|
|
90
|
+
return file_path
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# -----------------------------------------------------------------------------
|
|
94
|
+
# Generators
|
|
95
|
+
# -----------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
def write_eregime_sinusoidal(
|
|
98
|
+
file_path: Union[str, Path],
|
|
99
|
+
*,
|
|
100
|
+
max_magnitude: float,
|
|
101
|
+
step_angle: float,
|
|
102
|
+
iteration_step: int,
|
|
103
|
+
num_cycles: float,
|
|
104
|
+
direction: str = "z",
|
|
105
|
+
voltage_idx: int = 1,
|
|
106
|
+
phase: float = 0.0,
|
|
107
|
+
dc_offset: float = 0.0,
|
|
108
|
+
start_iter: int = 0,
|
|
109
|
+
) -> Path:
|
|
110
|
+
"""
|
|
111
|
+
Generate a sinusoidal electric-field schedule and write ``eregime.in``.
|
|
112
|
+
|
|
113
|
+
The generated field follows:
|
|
114
|
+
E(t) = dc_offset + max_magnitude · sin(phase + k · step_angle)
|
|
115
|
+
|
|
116
|
+
sampled at fixed angular increments.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
max_magnitude : float
|
|
121
|
+
Peak field amplitude in V/Å.
|
|
122
|
+
step_angle : float
|
|
123
|
+
Angular step size in radians.
|
|
124
|
+
iteration_step : int
|
|
125
|
+
Iteration increment between successive samples.
|
|
126
|
+
num_cycles : float
|
|
127
|
+
Total number of sinusoidal cycles.
|
|
128
|
+
direction : {"x","y","z"}, optional
|
|
129
|
+
Field direction (default: ``"z"``).
|
|
130
|
+
voltage_idx : int, optional
|
|
131
|
+
Voltage index column value (default: 1).
|
|
132
|
+
phase : float, optional
|
|
133
|
+
Phase offset in radians.
|
|
134
|
+
dc_offset : float, optional
|
|
135
|
+
Constant offset added to the field (V/Å).
|
|
136
|
+
start_iter : int, optional
|
|
137
|
+
Starting iteration index.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
Path
|
|
142
|
+
The written ``eregime.in`` file path.
|
|
143
|
+
|
|
144
|
+
Examples
|
|
145
|
+
---
|
|
146
|
+
>>> write_eregime_sinusoidal(
|
|
147
|
+
... "eregime.in",
|
|
148
|
+
... max_magnitude=0.05,
|
|
149
|
+
... step_angle=0.1,
|
|
150
|
+
... iteration_step=10,
|
|
151
|
+
... num_cycles=2
|
|
152
|
+
... )
|
|
153
|
+
"""
|
|
154
|
+
if step_angle <= 0:
|
|
155
|
+
raise ValueError("step_angle must be > 0")
|
|
156
|
+
if iteration_step <= 0:
|
|
157
|
+
raise ValueError("iteration_step must be > 0")
|
|
158
|
+
|
|
159
|
+
direction = _normalize_direction(direction)
|
|
160
|
+
npts = int(round((2.0 * num_cycles * math.pi) / step_angle)) + 1
|
|
161
|
+
|
|
162
|
+
rows = []
|
|
163
|
+
for k in range(npts):
|
|
164
|
+
ang = phase + k * step_angle
|
|
165
|
+
mag = dc_offset + max_magnitude * math.sin(ang)
|
|
166
|
+
it = start_iter + k * iteration_step
|
|
167
|
+
rows.append((it, voltage_idx, direction, float(mag)))
|
|
168
|
+
|
|
169
|
+
out = write_a_given_eregime(file_path, rows)
|
|
170
|
+
print(f"[Done] Sinusoidal eregime saved to {out} ({npts} entry rows).")
|
|
171
|
+
return out
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def write_eregime_smooth_pulse(
|
|
175
|
+
file_path: Union[str, Path],
|
|
176
|
+
*,
|
|
177
|
+
amplitude: float,
|
|
178
|
+
width: float,
|
|
179
|
+
period: float,
|
|
180
|
+
slope: float,
|
|
181
|
+
iteration_step: int,
|
|
182
|
+
num_of_cycles: Union[int, float],
|
|
183
|
+
step_size: float = 0.1,
|
|
184
|
+
direction: str = "z",
|
|
185
|
+
voltage_idx: int = 1,
|
|
186
|
+
baseline: float = 0.0,
|
|
187
|
+
start_iter: int = 0,
|
|
188
|
+
) -> Path:
|
|
189
|
+
"""
|
|
190
|
+
Generate smooth bipolar electric-field pulses and write ``eregime.in``.
|
|
191
|
+
|
|
192
|
+
Each cycle consists of a positive half-cycle followed by a mirrored
|
|
193
|
+
negative half-cycle, with linear ramps and flat plateaus.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
amplitude : float
|
|
198
|
+
Peak field magnitude in V/Å (positive value).
|
|
199
|
+
width : float
|
|
200
|
+
Flat-top duration at peak amplitude.
|
|
201
|
+
period : float
|
|
202
|
+
Full cycle duration.
|
|
203
|
+
slope : float
|
|
204
|
+
Ramp-up and ramp-down duration.
|
|
205
|
+
iteration_step : int
|
|
206
|
+
Iteration increment per sample.
|
|
207
|
+
num_of_cycles : int | float
|
|
208
|
+
Number of cycles to generate.
|
|
209
|
+
step_size : float, optional
|
|
210
|
+
Time resolution for sampling.
|
|
211
|
+
direction : {"x","y","z"}, optional
|
|
212
|
+
Field direction.
|
|
213
|
+
voltage_idx : int, optional
|
|
214
|
+
Voltage index column value.
|
|
215
|
+
baseline : float, optional
|
|
216
|
+
Baseline field offset in V/Å.
|
|
217
|
+
start_iter : int, optional
|
|
218
|
+
Starting iteration index.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
Path
|
|
223
|
+
The written ``eregime.in`` file path.
|
|
224
|
+
|
|
225
|
+
Examples
|
|
226
|
+
---
|
|
227
|
+
>>> write_eregime_smooth_pulse(
|
|
228
|
+
... "eregime.in",
|
|
229
|
+
... amplitude=0.1,
|
|
230
|
+
... width=5.0,
|
|
231
|
+
... period=20.0,
|
|
232
|
+
... slope=2.0,
|
|
233
|
+
... iteration_step=10,
|
|
234
|
+
... num_of_cycles=3
|
|
235
|
+
... )
|
|
236
|
+
"""
|
|
237
|
+
if period <= 0 or step_size <= 0 or slope < 0 or width < 0:
|
|
238
|
+
raise ValueError("period>0, step_size>0, slope>=0, width>=0 are required")
|
|
239
|
+
if 2 * slope + width > (period / 2):
|
|
240
|
+
raise ValueError("Each half-period must satisfy 2*slope + width <= period/2")
|
|
241
|
+
if iteration_step <= 0:
|
|
242
|
+
raise ValueError("iteration_step must be > 0")
|
|
243
|
+
|
|
244
|
+
direction = _normalize_direction(direction)
|
|
245
|
+
|
|
246
|
+
total_time = float(num_of_cycles) * period
|
|
247
|
+
t = np.arange(0.0, total_time + step_size, step_size)
|
|
248
|
+
halfT = period / 2.0
|
|
249
|
+
|
|
250
|
+
def half_profile(tin: float) -> float:
|
|
251
|
+
if tin < slope:
|
|
252
|
+
return baseline + (amplitude / slope) * tin if slope > 0 else baseline + amplitude
|
|
253
|
+
if tin < slope + width:
|
|
254
|
+
return baseline + amplitude
|
|
255
|
+
if tin < 2.0 * slope + width:
|
|
256
|
+
return baseline + amplitude - (amplitude / slope) * (tin - (slope + width)) if slope > 0 else baseline
|
|
257
|
+
return baseline
|
|
258
|
+
|
|
259
|
+
rows = []
|
|
260
|
+
for idx, ti in enumerate(t):
|
|
261
|
+
in_cycle = ti % period
|
|
262
|
+
sign = 1.0 if in_cycle < halfT else -1.0
|
|
263
|
+
tin = in_cycle if sign > 0 else (in_cycle - halfT)
|
|
264
|
+
mag = sign * (half_profile(tin) - baseline) + baseline
|
|
265
|
+
it = start_iter + idx * iteration_step
|
|
266
|
+
rows.append((it, voltage_idx, direction, float(mag)))
|
|
267
|
+
|
|
268
|
+
out = write_a_given_eregime(file_path, rows)
|
|
269
|
+
print(f"[Done] Smooth pulse eregime saved to {out} ({len(rows)} entry rows).")
|
|
270
|
+
return out
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def write_eregime_from_function(
|
|
274
|
+
file_path: Union[str, Path],
|
|
275
|
+
*,
|
|
276
|
+
func: Callable[[float], float],
|
|
277
|
+
t_end: float,
|
|
278
|
+
dt: float,
|
|
279
|
+
iteration_step: int,
|
|
280
|
+
direction: str = "z",
|
|
281
|
+
voltage_idx: int = 1,
|
|
282
|
+
start_iter: int = 0,
|
|
283
|
+
) -> Path:
|
|
284
|
+
"""
|
|
285
|
+
Generate an electric-field regime from an arbitrary function and
|
|
286
|
+
write ``eregime.in``.
|
|
287
|
+
|
|
288
|
+
The function ``func(t)`` is sampled uniformly in time and mapped
|
|
289
|
+
to simulation iterations.
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
func : Callable[[float], float]
|
|
294
|
+
Function returning electric-field magnitude (V/Å) at time ``t``.
|
|
295
|
+
t_end : float
|
|
296
|
+
End time for sampling.
|
|
297
|
+
dt : float
|
|
298
|
+
Time step for sampling.
|
|
299
|
+
iteration_step : int
|
|
300
|
+
Iteration increment per sample.
|
|
301
|
+
direction : {"x","y","z"}, optional
|
|
302
|
+
Field direction.
|
|
303
|
+
voltage_idx : int, optional
|
|
304
|
+
Voltage index column value.
|
|
305
|
+
start_iter : int, optional
|
|
306
|
+
Starting iteration index.
|
|
307
|
+
|
|
308
|
+
Returns
|
|
309
|
+
-------
|
|
310
|
+
Path
|
|
311
|
+
The written ``eregime.in`` file path.
|
|
312
|
+
|
|
313
|
+
Examples
|
|
314
|
+
---
|
|
315
|
+
>>> f = lambda t: 0.02 * t
|
|
316
|
+
>>> write_eregime_from_function(
|
|
317
|
+
... "eregime.in",
|
|
318
|
+
... func=f,
|
|
319
|
+
... t_end=10.0,
|
|
320
|
+
... dt=0.5,
|
|
321
|
+
... iteration_step=5
|
|
322
|
+
... )
|
|
323
|
+
"""
|
|
324
|
+
if dt <= 0 or t_end < 0:
|
|
325
|
+
raise ValueError("dt must be > 0 and t_end >= 0")
|
|
326
|
+
if iteration_step <= 0:
|
|
327
|
+
raise ValueError("iteration_step must be > 0")
|
|
328
|
+
|
|
329
|
+
direction = _normalize_direction(direction)
|
|
330
|
+
|
|
331
|
+
t = np.arange(0.0, t_end + dt, dt)
|
|
332
|
+
rows = []
|
|
333
|
+
for i, ti in enumerate(t):
|
|
334
|
+
mag = float(func(float(ti)))
|
|
335
|
+
it = start_iter + i * iteration_step
|
|
336
|
+
rows.append((it, voltage_idx, direction, mag))
|
|
337
|
+
|
|
338
|
+
out = write_a_given_eregime(file_path, rows)
|
|
339
|
+
print(f"[Done] Functional eregime saved to {out} ({len(rows)} entry rows).")
|
|
340
|
+
return out
|
|
341
|
+
|