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,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
+