lammpsparser 0.0.6__tar.gz → 0.0.8__tar.gz
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.
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/PKG-INFO +2 -2
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/pyproject.toml +2 -2
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/_version.py +2 -2
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/calculate.py +21 -45
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/data.py +11 -10
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/file.py +43 -17
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/output.py +4 -1
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/structure.py +70 -65
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/output.py +36 -38
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/output_raw.py +21 -22
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/potential.py +8 -9
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/structure.py +50 -69
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/units.py +10 -11
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/.gitignore +0 -0
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/LICENSE +0 -0
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/README.md +0 -0
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/__init__.py +0 -0
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/__init__.py +0 -0
- {lammpsparser-0.0.6 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/constraints.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lammpsparser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: Vector-oriented LAMMPS interface to rapidly iterate over series of atomistic structures or interatomic potentials.
|
|
5
5
|
Project-URL: Homepage, https://github.com/pyiron/lammpsparser
|
|
6
6
|
Project-URL: Documentation, https://github.com/pyiron/lammpsparser
|
|
@@ -51,7 +51,7 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
|
51
51
|
Requires-Python: <3.15,>=3.9
|
|
52
52
|
Requires-Dist: ase<=3.29.0,>=3.22.1
|
|
53
53
|
Requires-Dist: numpy<=2.4.6,>=1.26.0
|
|
54
|
-
Requires-Dist: pandas<=3.0.
|
|
54
|
+
Requires-Dist: pandas<=3.0.5,>=2.0.3
|
|
55
55
|
Requires-Dist: scipy<=1.17.1,>=1.11.1
|
|
56
56
|
Provides-Extra: compatibility
|
|
57
57
|
Requires-Dist: structuretoolkit<=0.0.45,>=0.0.30; extra == 'compatibility'
|
|
@@ -5,7 +5,7 @@ requires = [
|
|
|
5
5
|
"h5py",
|
|
6
6
|
"pandas",
|
|
7
7
|
"scipy",
|
|
8
|
-
"hatchling==1.
|
|
8
|
+
"hatchling==1.31.0",
|
|
9
9
|
"hatch-vcs==0.5.0",
|
|
10
10
|
]
|
|
11
11
|
build-backend = "hatchling.build"
|
|
@@ -36,7 +36,7 @@ classifiers = [
|
|
|
36
36
|
dependencies = [
|
|
37
37
|
"ase>=3.22.1,<=3.29.0",
|
|
38
38
|
"numpy>=1.26.0,<=2.4.6",
|
|
39
|
-
"pandas>=2.0.3,<=3.0.
|
|
39
|
+
"pandas>=2.0.3,<=3.0.5",
|
|
40
40
|
"scipy>=1.11.1,<=1.17.1",
|
|
41
41
|
]
|
|
42
42
|
dynamic = ["version"]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
21
|
+
__version__ = version = '0.0.8'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 8)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -64,10 +64,10 @@ def calc_md(
|
|
|
64
64
|
delta_press (float): Barostat timescale, but in your Lammps time units, whatever those
|
|
65
65
|
are. (DEPRECATED.)
|
|
66
66
|
job_name (str): Job name of the job to generate a unique random seed.
|
|
67
|
-
rotation_matrix (numpy.ndarray): The rotation matrix from the
|
|
67
|
+
rotation_matrix (numpy.ndarray): The rotation matrix from the ASE to Lammps coordinate
|
|
68
68
|
frame.
|
|
69
69
|
"""
|
|
70
|
-
if units not in LAMMPS_UNIT_CONVERSIONS
|
|
70
|
+
if units not in LAMMPS_UNIT_CONVERSIONS:
|
|
71
71
|
raise NotImplementedError
|
|
72
72
|
time_units = LAMMPS_UNIT_CONVERSIONS[units]["time"]
|
|
73
73
|
temperature_units = LAMMPS_UNIT_CONVERSIONS[units]["temperature"]
|
|
@@ -103,7 +103,7 @@ def calc_md(
|
|
|
103
103
|
raise ValueError(
|
|
104
104
|
"At most two temperatures can be provided "
|
|
105
105
|
"(for a linearly ramping target temperature), "
|
|
106
|
-
"but got {
|
|
106
|
+
f"but got {len(temperature)}"
|
|
107
107
|
)
|
|
108
108
|
temperature *= temperature_units
|
|
109
109
|
|
|
@@ -128,35 +128,24 @@ def calc_md(
|
|
|
128
128
|
)
|
|
129
129
|
|
|
130
130
|
if np.isscalar(pressure):
|
|
131
|
-
pressure_string = " iso {
|
|
132
|
-
pressure, pressure_damping_timescale
|
|
133
|
-
)
|
|
131
|
+
pressure_string = f" iso {pressure} {pressure} {pressure_damping_timescale}"
|
|
134
132
|
else:
|
|
135
133
|
pressure_string = ""
|
|
136
134
|
for ii, (coord, value) in enumerate(
|
|
137
135
|
zip(["x", "y", "z", "xy", "xz", "yz"], pressure)
|
|
138
136
|
):
|
|
139
137
|
if value is not None:
|
|
140
|
-
pressure_string +=
|
|
141
|
-
coord
|
|
138
|
+
pressure_string += (
|
|
139
|
+
f" {coord} {value} {value} {pressure_damping_timescale}"
|
|
142
140
|
)
|
|
143
141
|
# if ii > 2:
|
|
144
142
|
# force_skewed = True
|
|
145
143
|
|
|
146
144
|
if langevin: # NPT(Langevin)
|
|
147
145
|
fix_ensemble_str = "fix ensemble all nph" + pressure_string
|
|
148
|
-
thermo_str = "fix langevin all langevin {0} {1} {
|
|
149
|
-
str(temperature[0]),
|
|
150
|
-
str(temperature[1]),
|
|
151
|
-
str(temperature_damping_timescale),
|
|
152
|
-
str(seed),
|
|
153
|
-
)
|
|
146
|
+
thermo_str = f"fix langevin all langevin {temperature[0]!s} {temperature[1]!s} {temperature_damping_timescale!s} {seed!s} zero yes"
|
|
154
147
|
else: # NPT(Nose-Hoover)
|
|
155
|
-
fix_ensemble_str = "fix ensemble all npt temp {0} {1} {
|
|
156
|
-
str(temperature[0]),
|
|
157
|
-
str(temperature[1]),
|
|
158
|
-
str(temperature_damping_timescale),
|
|
159
|
-
)
|
|
148
|
+
fix_ensemble_str = f"fix ensemble all npt temp {temperature[0]!s} {temperature[1]!s} {temperature_damping_timescale!s}"
|
|
160
149
|
fix_ensemble_str += pressure_string
|
|
161
150
|
elif temperature is not None: # NVT
|
|
162
151
|
if temperature.min() <= 0:
|
|
@@ -166,18 +155,9 @@ def calc_md(
|
|
|
166
155
|
|
|
167
156
|
if langevin: # NVT(Langevin)
|
|
168
157
|
fix_ensemble_str = "fix ensemble all nve"
|
|
169
|
-
thermo_str = "fix langevin all langevin {0} {1} {
|
|
170
|
-
str(temperature[0]),
|
|
171
|
-
str(temperature[1]),
|
|
172
|
-
str(temperature_damping_timescale),
|
|
173
|
-
str(seed),
|
|
174
|
-
)
|
|
158
|
+
thermo_str = f"fix langevin all langevin {temperature[0]!s} {temperature[1]!s} {temperature_damping_timescale!s} {seed!s} zero yes"
|
|
175
159
|
else: # NVT(Nose-Hoover)
|
|
176
|
-
fix_ensemble_str = "fix ensemble all nvt temp {0} {1} {
|
|
177
|
-
str(temperature[0]),
|
|
178
|
-
str(temperature[1]),
|
|
179
|
-
str(temperature_damping_timescale),
|
|
180
|
-
)
|
|
160
|
+
fix_ensemble_str = f"fix ensemble all nvt temp {temperature[0]!s} {temperature[1]!s} {temperature_damping_timescale!s}"
|
|
181
161
|
else: # NVE
|
|
182
162
|
if langevin:
|
|
183
163
|
warnings.warn("Temperature not set; Langevin ignored.")
|
|
@@ -190,8 +170,8 @@ def calc_md(
|
|
|
190
170
|
if thermo_str != "":
|
|
191
171
|
line_lst.append(thermo_str)
|
|
192
172
|
|
|
193
|
-
line_lst.append("variable thermotime equal {} "
|
|
194
|
-
line_lst.append("timestep {}"
|
|
173
|
+
line_lst.append(f"variable thermotime equal {n_print} ")
|
|
174
|
+
line_lst.append(f"timestep {time_step}")
|
|
195
175
|
if initial_temperature is not None and initial_temperature > 0:
|
|
196
176
|
line_lst.append(
|
|
197
177
|
_set_initial_velocity(
|
|
@@ -241,18 +221,14 @@ def calc_minimize(
|
|
|
241
221
|
style ('cg'/'sd'/other values from Lammps docs): The style of the numeric minimization, either conjugate
|
|
242
222
|
gradient, steepest descent, or other keys permissible from the Lammps docs on 'min_style'. (Default
|
|
243
223
|
is 'cg' -- conjugate gradient.)
|
|
244
|
-
rotation_matrix (numpy.ndarray): The rotation matrix from the
|
|
224
|
+
rotation_matrix (numpy.ndarray): The rotation matrix from the ASE to Lammps coordinate frame.
|
|
245
225
|
"""
|
|
246
|
-
# This docstring is a source for the calc_minimize method in pyiron_atomistics.lammps.base.LammpsBase.calc_minimize and
|
|
247
|
-
# pyiron_atomistics.lammps.interactive.LammpsInteractive.calc_minimize -- Please ensure that changes to signature or
|
|
248
|
-
# defaults stay consistent!
|
|
249
|
-
|
|
250
226
|
max_evaluations = 100 * max_iter
|
|
251
227
|
if n_print > max_iter:
|
|
252
228
|
warnings.warn("n_print larger than max_iter, adjusting to n_print=max_iter")
|
|
253
229
|
n_print = max_iter
|
|
254
230
|
|
|
255
|
-
if units not in LAMMPS_UNIT_CONVERSIONS
|
|
231
|
+
if units not in LAMMPS_UNIT_CONVERSIONS:
|
|
256
232
|
raise NotImplementedError
|
|
257
233
|
energy_units = LAMMPS_UNIT_CONVERSIONS[units]["energy"]
|
|
258
234
|
force_units = LAMMPS_UNIT_CONVERSIONS[units]["force"]
|
|
@@ -260,7 +236,7 @@ def calc_minimize(
|
|
|
260
236
|
ionic_energy_tolerance *= energy_units
|
|
261
237
|
ionic_force_tolerance *= force_units
|
|
262
238
|
|
|
263
|
-
line_lst = ["variable thermotime equal {} "
|
|
239
|
+
line_lst = [f"variable thermotime equal {n_print} "]
|
|
264
240
|
line_lst += _get_thermo()
|
|
265
241
|
if pressure is not None:
|
|
266
242
|
if rotation_matrix is None:
|
|
@@ -278,14 +254,14 @@ def calc_minimize(
|
|
|
278
254
|
pressure=pressure, rotation_matrix=rotation_matrix, units=units
|
|
279
255
|
)
|
|
280
256
|
if np.isscalar(pressure):
|
|
281
|
-
str_press = " iso {
|
|
257
|
+
str_press = f" iso {float(cast(Any, pressure))}"
|
|
282
258
|
else:
|
|
283
259
|
str_press = ""
|
|
284
260
|
for ii, (press, str_axis) in enumerate(
|
|
285
261
|
zip(pressure, ["x", "y", "z", "xy", "xz", "yz"])
|
|
286
262
|
):
|
|
287
263
|
if press is not None:
|
|
288
|
-
str_press += " {} {}"
|
|
264
|
+
str_press += f" {str_axis} {press}"
|
|
289
265
|
# if ii > 2:
|
|
290
266
|
# force_skewed = True
|
|
291
267
|
if len(str_press) > 1:
|
|
@@ -407,11 +383,11 @@ def _pressure_to_lammps(pressure, rotation_matrix, units="metal"):
|
|
|
407
383
|
In case of a single pressure value, it is again returned as a single pressure value, to be used with the "iso"
|
|
408
384
|
option (i.e., coupled deformation in x, y, and z).
|
|
409
385
|
|
|
410
|
-
Finally, we also ensure that the units are converted from
|
|
386
|
+
Finally, we also ensure that the units are converted from lammpsparser's GPa to whatever Lammps needs.
|
|
411
387
|
|
|
412
388
|
Args:
|
|
413
389
|
pressure (float/list/tuple/numpy.ndarray): The pressure(s) to convert.
|
|
414
|
-
rotation_matrix (numpy.ndarray): The 3x3 matrix rotating from the
|
|
390
|
+
rotation_matrix (numpy.ndarray): The 3x3 matrix rotating from the ASE to Lammps coordinate frame.
|
|
415
391
|
|
|
416
392
|
Returns:
|
|
417
393
|
(list): pxx, pyy, pzz, pxy, pxz, and pyz to be passed to Lammps.
|
|
@@ -433,7 +409,7 @@ def _pressure_to_lammps(pressure, rotation_matrix, units="metal"):
|
|
|
433
409
|
if len(pressure) > 6:
|
|
434
410
|
raise ValueError(
|
|
435
411
|
"Pressure can have a maximum of 6 values, (x, y, z, xy, xz, and yz), but got "
|
|
436
|
-
+ "{
|
|
412
|
+
+ f"{len(pressure)}"
|
|
437
413
|
)
|
|
438
414
|
pressure = [float(p) if p is not None else None for p in pressure]
|
|
439
415
|
pressure += (6 - len(pressure)) * [None]
|
|
@@ -451,7 +427,7 @@ def _pressure_to_lammps(pressure, rotation_matrix, units="metal"):
|
|
|
451
427
|
"Cells which are not orthorhombic or an upper-triangular cell are incompatible with Lammps "
|
|
452
428
|
"constant pressure calculations unless the entire pressure tensor is defined. "
|
|
453
429
|
"The reason is that Lammps demands such cells be represented with an "
|
|
454
|
-
"upper-triangular unit cell, thus a rotation between Lammps and
|
|
430
|
+
"upper-triangular unit cell, thus a rotation between Lammps and ASE coordinate "
|
|
455
431
|
"frames is required; it is not possible to rotate the pressure tensor if any of "
|
|
456
432
|
"its components is None."
|
|
457
433
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Optional, Union
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
5
6
|
|
|
@@ -41,20 +42,20 @@ class CalcMDInput:
|
|
|
41
42
|
frame. Derived automatically when ``None``.
|
|
42
43
|
"""
|
|
43
44
|
|
|
44
|
-
temperature:
|
|
45
|
-
pressure:
|
|
45
|
+
temperature: float | list | None = None
|
|
46
|
+
pressure: float | list | np.ndarray | None = None
|
|
46
47
|
n_ionic_steps: int = 1
|
|
47
48
|
time_step: float = 1.0
|
|
48
49
|
n_print: int = 100
|
|
49
50
|
temperature_damping_timescale: float = 100.0
|
|
50
51
|
pressure_damping_timescale: float = 1000.0
|
|
51
52
|
seed: int = 80996
|
|
52
|
-
tloop:
|
|
53
|
-
initial_temperature:
|
|
53
|
+
tloop: int | None = None
|
|
54
|
+
initial_temperature: float | None = None
|
|
54
55
|
langevin: bool = False
|
|
55
|
-
delta_temp:
|
|
56
|
-
delta_press:
|
|
57
|
-
rotation_matrix:
|
|
56
|
+
delta_temp: float | None = None
|
|
57
|
+
delta_press: float | None = None
|
|
58
|
+
rotation_matrix: list | np.ndarray | None = None
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
@dataclass
|
|
@@ -81,7 +82,7 @@ class CalcMinimizeInput:
|
|
|
81
82
|
ionic_energy_tolerance: float = 0.0
|
|
82
83
|
ionic_force_tolerance: float = 1e-4
|
|
83
84
|
max_iter: int = 100000
|
|
84
|
-
pressure:
|
|
85
|
+
pressure: float | list | np.ndarray | None = None
|
|
85
86
|
n_print: int = 1
|
|
86
87
|
style: str = "cg"
|
|
87
|
-
rotation_matrix:
|
|
88
|
+
rotation_matrix: list | np.ndarray | None = None
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
4
|
import shutil
|
|
3
5
|
import subprocess
|
|
4
6
|
from dataclasses import asdict
|
|
5
|
-
from typing import Optional, Union
|
|
6
7
|
|
|
7
8
|
import pandas
|
|
8
9
|
from ase.atoms import Atoms
|
|
@@ -24,26 +25,27 @@ def lammps_file_interface_function(
|
|
|
24
25
|
structure: Atoms,
|
|
25
26
|
potential: str,
|
|
26
27
|
calc_mode: str = "static",
|
|
27
|
-
calc_kwargs:
|
|
28
|
-
calc_dataclass:
|
|
28
|
+
calc_kwargs: dict | None = None,
|
|
29
|
+
calc_dataclass: CalcMDInput | CalcMinimizeInput | None = None,
|
|
29
30
|
units: str = "metal",
|
|
30
|
-
lmp_command:
|
|
31
|
-
resource_path:
|
|
32
|
-
input_control_file:
|
|
31
|
+
lmp_command: str | None = None,
|
|
32
|
+
resource_path: str | None = None,
|
|
33
|
+
input_control_file: dict | None = None,
|
|
33
34
|
write_restart_file: bool = False,
|
|
34
35
|
read_restart_file: bool = False,
|
|
35
36
|
restart_file: str = "restart.out",
|
|
37
|
+
dump_final_structure: bool = False,
|
|
36
38
|
):
|
|
37
39
|
"""
|
|
38
|
-
A single function to execute a LAMMPS calculation based on the LAMMPS job implemented in
|
|
40
|
+
A single function to execute a LAMMPS calculation based on the LAMMPS job interface implemented in lammpsparser
|
|
39
41
|
|
|
40
42
|
Examples:
|
|
41
43
|
|
|
42
44
|
>>> import os
|
|
43
45
|
>>> from ase.build import bulk
|
|
44
|
-
>>> from
|
|
46
|
+
>>> from lammpsparser.compatibility.file import lammps_file_interface_function
|
|
45
47
|
>>>
|
|
46
|
-
>>> shell_output, parsed_output, job_crashed =
|
|
48
|
+
>>> shell_output, parsed_output, job_crashed = lammps_file_interface_function(
|
|
47
49
|
... working_directory=os.path.abspath("lmp_working_directory"),
|
|
48
50
|
... structure=bulk("Al", cubic=True),
|
|
49
51
|
... potential='2009--Mendelev-M-I--Al-Mg--LAMMPS--ipr1',
|
|
@@ -85,6 +87,8 @@ def lammps_file_interface_function(
|
|
|
85
87
|
write_restart_file (bool): enable writing the LAMMPS restart file
|
|
86
88
|
read_restart_file (bool): enable loading the LAMMPS restart file
|
|
87
89
|
restart_file (str): file name of the LAMMPS restart file to copy
|
|
90
|
+
dump_final_structure (bool): in "md" mode, append a ``write_dump`` command after the ``run`` command to capture
|
|
91
|
+
the final structure in ``dump.out`` if last step is not a regular dump step. Disabled by default.
|
|
88
92
|
executable_version (str): LAMMPS version to for the execution
|
|
89
93
|
executable_path (str): path to the LAMMPS executable
|
|
90
94
|
input_control_file (str|list|dict): Option to modify the LAMMPS input file directly
|
|
@@ -141,14 +145,21 @@ def lammps_file_interface_function(
|
|
|
141
145
|
else:
|
|
142
146
|
lmp_str_lst.append(line)
|
|
143
147
|
|
|
148
|
+
dump_fields = "id type xsu ysu zsu fx fy fz vx vy vz"
|
|
149
|
+
dump_format = '"%d %d %20.15g %20.15g %20.15g %20.15g %20.15g %20.15g %20.15g %20.15g %20.15g"'
|
|
150
|
+
|
|
144
151
|
lmp_str_lst += potential_lst
|
|
145
152
|
lmp_str_lst += ["variable dumptime equal {} ".format(calc_kwargs.get("n_print", 1))]
|
|
146
153
|
lmp_str_lst += [
|
|
147
|
-
"dump 1 all custom ${dumptime} dump.out
|
|
148
|
-
|
|
154
|
+
"dump 1 all custom ${dumptime} dump.out " + dump_fields,
|
|
155
|
+
"dump_modify 1 sort id format line " + dump_format,
|
|
149
156
|
]
|
|
150
157
|
|
|
151
158
|
if calc_mode == "static":
|
|
159
|
+
if dump_final_structure:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
"dump_final_structure is only applicable for molecular dynamics (md) calculations."
|
|
162
|
+
)
|
|
152
163
|
lmp_str_constraint_lst = [
|
|
153
164
|
k + " " + v
|
|
154
165
|
for k, v in set_selective_dynamics(
|
|
@@ -168,7 +179,7 @@ def lammps_file_interface_function(
|
|
|
168
179
|
structure=structure, calc_md=True
|
|
169
180
|
).items()
|
|
170
181
|
]
|
|
171
|
-
if "n_ionic_steps" in calc_kwargs
|
|
182
|
+
if "n_ionic_steps" in calc_kwargs:
|
|
172
183
|
n_ionic_steps = int(calc_kwargs.pop("n_ionic_steps"))
|
|
173
184
|
else:
|
|
174
185
|
n_ionic_steps = 1
|
|
@@ -182,8 +193,21 @@ def lammps_file_interface_function(
|
|
|
182
193
|
)
|
|
183
194
|
if read_restart_file:
|
|
184
195
|
lmp_str_lst += ["reset_timestep 0"]
|
|
185
|
-
lmp_str_lst += ["run {} "
|
|
196
|
+
lmp_str_lst += [f"run {n_ionic_steps} "]
|
|
197
|
+
last_step_is_regular_dump = n_ionic_steps % calc_kwargs.get("n_print", 1) == 0
|
|
198
|
+
if dump_final_structure and not last_step_is_regular_dump:
|
|
199
|
+
lmp_str_lst += [
|
|
200
|
+
"write_dump all custom dump.out "
|
|
201
|
+
+ dump_fields
|
|
202
|
+
+ " modify sort id format line "
|
|
203
|
+
+ dump_format
|
|
204
|
+
+ " append yes"
|
|
205
|
+
]
|
|
186
206
|
elif calc_mode == "minimize":
|
|
207
|
+
if dump_final_structure:
|
|
208
|
+
raise ValueError(
|
|
209
|
+
"dump_final_structure is only applicable for molecular dynamics (md) calculations."
|
|
210
|
+
)
|
|
187
211
|
calc_kwargs["units"] = units
|
|
188
212
|
lmp_str_constraint_lst = [
|
|
189
213
|
k + " " + v
|
|
@@ -282,8 +306,8 @@ def lammps_file_initialization(
|
|
|
282
306
|
|
|
283
307
|
|
|
284
308
|
def _modify_input_dict(
|
|
285
|
-
input_control_file:
|
|
286
|
-
lmp_str_lst: list[str] =
|
|
309
|
+
input_control_file: dict | None = None,
|
|
310
|
+
lmp_str_lst: list[str] | None = None,
|
|
287
311
|
):
|
|
288
312
|
"""
|
|
289
313
|
Apply user-supplied overrides to a LAMMPS input line list.
|
|
@@ -302,13 +326,15 @@ def _modify_input_dict(
|
|
|
302
326
|
Returns:
|
|
303
327
|
list[str]: Modified (or unchanged) list of LAMMPS input lines.
|
|
304
328
|
"""
|
|
329
|
+
if lmp_str_lst is None:
|
|
330
|
+
lmp_str_lst = []
|
|
305
331
|
if input_control_file is not None:
|
|
306
332
|
lmp_tmp_lst, keys_used = [], []
|
|
307
333
|
for line in lmp_str_lst:
|
|
308
334
|
ls = line.split()
|
|
309
335
|
if len(ls) >= 1: # Remove empty lines
|
|
310
336
|
key = ls[0]
|
|
311
|
-
if key in input_control_file
|
|
337
|
+
if key in input_control_file:
|
|
312
338
|
lmp_tmp_lst.append(key + " " + input_control_file[key])
|
|
313
339
|
keys_used.append(key)
|
|
314
340
|
else:
|
|
@@ -322,7 +348,7 @@ def _modify_input_dict(
|
|
|
322
348
|
return lmp_str_lst
|
|
323
349
|
|
|
324
350
|
|
|
325
|
-
def _get_potential(potential, resource_path:
|
|
351
|
+
def _get_potential(potential, resource_path: str | None = None):
|
|
326
352
|
"""
|
|
327
353
|
Resolve a potential specification to LAMMPS input lines and species list.
|
|
328
354
|
|
|
@@ -6,6 +6,7 @@ def structure_from_parsed_output(
|
|
|
6
6
|
parsed_output: dict,
|
|
7
7
|
*,
|
|
8
8
|
wrap: bool = False,
|
|
9
|
+
scale_atoms: bool = True,
|
|
9
10
|
index: int = -1,
|
|
10
11
|
) -> Atoms:
|
|
11
12
|
"""Construct an `Atoms` object from parsed output data.
|
|
@@ -27,10 +28,12 @@ def structure_from_parsed_output(
|
|
|
27
28
|
"""
|
|
28
29
|
# Take a copy of the initial structure as template and update the relevant properties
|
|
29
30
|
atoms_copy = initial_structure.copy()
|
|
31
|
+
atoms_copy.set_cell(
|
|
32
|
+
parsed_output["generic"]["cells"][index], scale_atoms=scale_atoms
|
|
33
|
+
)
|
|
30
34
|
atoms_copy.set_array("indices", parsed_output["generic"]["indices"][index])
|
|
31
35
|
atoms_copy.set_positions(parsed_output["generic"]["positions"][index])
|
|
32
36
|
atoms_copy.set_velocities(parsed_output["generic"]["velocities"][index])
|
|
33
|
-
atoms_copy.set_cell(parsed_output["generic"]["cells"][index])
|
|
34
37
|
atoms_copy.set_pbc(True)
|
|
35
38
|
if wrap:
|
|
36
39
|
atoms_copy.wrap()
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from collections import OrderedDict
|
|
2
|
-
from typing import
|
|
4
|
+
from typing import cast
|
|
3
5
|
|
|
4
6
|
import numpy as np
|
|
5
7
|
from ase.atoms import Atoms
|
|
@@ -12,15 +14,15 @@ from lammpsparser.structure import LammpsStructure
|
|
|
12
14
|
class LammpsStructureCompatibility(LammpsStructure):
|
|
13
15
|
def __init__(
|
|
14
16
|
self,
|
|
15
|
-
bond_dict:
|
|
17
|
+
bond_dict: dict | None = None,
|
|
16
18
|
units: str = "metal",
|
|
17
19
|
atom_type: str = "atomic",
|
|
18
20
|
):
|
|
19
21
|
super().__init__(bond_dict=bond_dict, units=units, atom_type=atom_type)
|
|
20
|
-
self._molecule_ids: list[int] = []
|
|
22
|
+
self._molecule_ids: list[int] | None = []
|
|
21
23
|
|
|
22
24
|
@property
|
|
23
|
-
def structure(self) ->
|
|
25
|
+
def structure(self) -> Atoms | None:
|
|
24
26
|
"""The ASE :class:`~ase.atoms.Atoms` object associated with this instance."""
|
|
25
27
|
return self._structure
|
|
26
28
|
|
|
@@ -37,17 +39,28 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
37
39
|
structure (ase.atoms.Atoms): The structure to serialise.
|
|
38
40
|
"""
|
|
39
41
|
self._structure = structure
|
|
40
|
-
if self.
|
|
42
|
+
if self._atom_type == "full":
|
|
41
43
|
input_str = self.structure_full()
|
|
42
|
-
elif self.
|
|
44
|
+
elif self._atom_type == "bond":
|
|
43
45
|
input_str = self.structure_bond()
|
|
44
|
-
elif self.
|
|
46
|
+
elif self._atom_type == "charge":
|
|
45
47
|
input_str = self.structure_charge()
|
|
46
|
-
else: # self.
|
|
48
|
+
else: # self._atom_type == 'atomic'
|
|
47
49
|
input_str = self.structure_atomic()
|
|
48
50
|
|
|
49
51
|
self._string_input = input_str + self._get_velocities_input_string()
|
|
50
52
|
|
|
53
|
+
@property
|
|
54
|
+
def molecule_ids(self) -> list[int] | np.ndarray:
|
|
55
|
+
"""Per-atom molecule IDs; defaults to all atoms in a single molecule."""
|
|
56
|
+
if self._molecule_ids is None or len(self._molecule_ids) == 0:
|
|
57
|
+
return np.ones(len(cast(Atoms, self._structure)), dtype=int)
|
|
58
|
+
return self._molecule_ids
|
|
59
|
+
|
|
60
|
+
@molecule_ids.setter
|
|
61
|
+
def molecule_ids(self, molecule_ids: list[int] | None):
|
|
62
|
+
self._molecule_ids = molecule_ids
|
|
63
|
+
|
|
51
64
|
def structure_bond(self):
|
|
52
65
|
"""
|
|
53
66
|
Serialise the structure for LAMMPS ``atom_style bond``.
|
|
@@ -120,7 +133,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
120
133
|
bond_type[i, j] = count
|
|
121
134
|
bond_type[j, i] = count
|
|
122
135
|
|
|
123
|
-
if self.structure
|
|
136
|
+
if getattr(self.structure, "bonds", None) is None:
|
|
124
137
|
if self.cutoff_radius is None:
|
|
125
138
|
bonds_lst = get_bonds(structure=self.structure, max_shells=1)
|
|
126
139
|
else:
|
|
@@ -143,9 +156,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
143
156
|
|
|
144
157
|
bonds_str = "Bonds \n\n"
|
|
145
158
|
for i_bond, (i_a, i_b, b_type) in enumerate(bonds):
|
|
146
|
-
bonds_str +=
|
|
147
|
-
"{0:d} {1:d} {2:d} {3:d}".format(i_bond + 1, b_type, i_a, i_b) + "\n"
|
|
148
|
-
)
|
|
159
|
+
bonds_str += f"{i_bond + 1:d} {b_type:d} {i_a:d} {i_b:d}" + "\n"
|
|
149
160
|
|
|
150
161
|
return (
|
|
151
162
|
self.lammps_header(
|
|
@@ -182,17 +193,23 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
182
193
|
coords = self.rotate_positions(self._structure)
|
|
183
194
|
|
|
184
195
|
# extract electric charges from potential file
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
196
|
+
if self.potential is not None:
|
|
197
|
+
q_dict = {
|
|
198
|
+
species_name: self.potential.get_charge(species_name)
|
|
199
|
+
for species_name in set(self.structure.get_chemical_symbols())
|
|
200
|
+
}
|
|
201
|
+
else:
|
|
202
|
+
q_dict = {
|
|
203
|
+
species_name: 0.0
|
|
204
|
+
for species_name in set(self.structure.get_chemical_symbols())
|
|
205
|
+
}
|
|
189
206
|
|
|
190
207
|
bonds_lst, angles_lst = [], []
|
|
191
208
|
bond_type_lst, angle_type_lst = [], []
|
|
192
209
|
# Using a cutoff distance to draw the bonds instead of the number of neighbors
|
|
193
210
|
# Only if any bonds are defined
|
|
194
|
-
if len(self._bond_dict.keys()) > 0:
|
|
195
|
-
cutoff_list =
|
|
211
|
+
if self._bond_dict is not None and len(self._bond_dict.keys()) > 0:
|
|
212
|
+
cutoff_list = []
|
|
196
213
|
for val in self._bond_dict.values():
|
|
197
214
|
cutoff_list.append(np.max(val["cutoff_list"]))
|
|
198
215
|
max_cutoff = np.max(cutoff_list)
|
|
@@ -205,45 +222,42 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
205
222
|
# Go through all elements for which bonds are defined
|
|
206
223
|
for element, val in self._bond_dict.items():
|
|
207
224
|
el_1_list = select_index(structure=self._structure, element=element)
|
|
208
|
-
if el_1_list is not None:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
225
|
+
if el_1_list is not None and len(el_1_list) > 0:
|
|
226
|
+
for i, v in enumerate(val["element_list"]):
|
|
227
|
+
el_2_list = select_index(structure=self._structure, element=v)
|
|
228
|
+
cutoff_dist = val["cutoff_list"][i]
|
|
229
|
+
for j, ind in enumerate(
|
|
230
|
+
np.array(neighbors.indices, dtype=object)[el_1_list]
|
|
231
|
+
):
|
|
232
|
+
# Only chose those indices within the cutoff distance and which belong
|
|
233
|
+
# to the species defined in the element_list
|
|
234
|
+
# i is the index of each bond type, and j is the element index
|
|
235
|
+
id_el = el_1_list[j]
|
|
236
|
+
bool_1 = (
|
|
237
|
+
np.array(neighbors.distances, dtype=object)[id_el]
|
|
238
|
+
<= cutoff_dist
|
|
213
239
|
)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
240
|
+
act_ind = ind[bool_1]
|
|
241
|
+
bool_2 = np.isin(act_ind, el_2_list)
|
|
242
|
+
final_ind = act_ind[bool_2]
|
|
243
|
+
# Get the bond and angle type
|
|
244
|
+
bond_type = val["bond_type_list"][i]
|
|
245
|
+
angle_type = val["angle_type_list"][i]
|
|
246
|
+
# Draw only maximum allowed bonds
|
|
247
|
+
final_ind = final_ind[: val["max_bond_list"][i]]
|
|
248
|
+
for fi in final_ind:
|
|
249
|
+
bonds_lst.append([id_el + 1, fi + 1])
|
|
250
|
+
bond_type_lst.append(bond_type)
|
|
251
|
+
# Draw angles if at least 2 bonds are present and if an angle type is defined for this
|
|
252
|
+
# particular set of bonds
|
|
253
|
+
if (
|
|
254
|
+
len(final_ind) >= 2
|
|
255
|
+
and val["angle_type_list"][i] is not None
|
|
217
256
|
):
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
# i is the index of each bond type, and j is the element index
|
|
221
|
-
id_el = el_1_list[j]
|
|
222
|
-
bool_1 = (
|
|
223
|
-
np.array(neighbors.distances, dtype=object)[id_el]
|
|
224
|
-
<= cutoff_dist
|
|
257
|
+
angles_lst.append(
|
|
258
|
+
[final_ind[0] + 1, id_el + 1, final_ind[1] + 1]
|
|
225
259
|
)
|
|
226
|
-
|
|
227
|
-
bool_2 = np.isin(act_ind, el_2_list)
|
|
228
|
-
final_ind = act_ind[bool_2]
|
|
229
|
-
# Get the bond and angle type
|
|
230
|
-
bond_type = val["bond_type_list"][i]
|
|
231
|
-
angle_type = val["angle_type_list"][i]
|
|
232
|
-
# Draw only maximum allowed bonds
|
|
233
|
-
final_ind = final_ind[: val["max_bond_list"][i]]
|
|
234
|
-
for fi in final_ind:
|
|
235
|
-
bonds_lst.append([id_el + 1, fi + 1])
|
|
236
|
-
bond_type_lst.append(bond_type)
|
|
237
|
-
# Draw angles if at least 2 bonds are present and if an angle type is defined for this
|
|
238
|
-
# particular set of bonds
|
|
239
|
-
if (
|
|
240
|
-
len(final_ind) >= 2
|
|
241
|
-
and val["angle_type_list"][i] is not None
|
|
242
|
-
):
|
|
243
|
-
angles_lst.append(
|
|
244
|
-
[final_ind[0] + 1, id_el + 1, final_ind[1] + 1]
|
|
245
|
-
)
|
|
246
|
-
angle_type_lst.append(angle_type)
|
|
260
|
+
angle_type_lst.append(angle_type)
|
|
247
261
|
|
|
248
262
|
if len(bond_type_lst) == 0:
|
|
249
263
|
num_bond_types = 0
|
|
@@ -277,9 +291,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
277
291
|
bonds_str = "Bonds \n\n"
|
|
278
292
|
for i_bond, id_vec in enumerate(bonds_lst):
|
|
279
293
|
bonds_str += (
|
|
280
|
-
"{
|
|
281
|
-
i_bond + 1, bond_type_lst[i_bond], id_vec[0], id_vec[1]
|
|
282
|
-
)
|
|
294
|
+
f"{i_bond + 1:d} {bond_type_lst[i_bond]:d} {id_vec[0]:d} {id_vec[1]:d}"
|
|
283
295
|
+ "\n"
|
|
284
296
|
)
|
|
285
297
|
else:
|
|
@@ -289,13 +301,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
289
301
|
angles_str = "Angles \n\n"
|
|
290
302
|
for i_angle, id_vec in enumerate(angles_lst):
|
|
291
303
|
angles_str += (
|
|
292
|
-
"{
|
|
293
|
-
i_angle + 1,
|
|
294
|
-
angle_type_lst[i_angle],
|
|
295
|
-
id_vec[0],
|
|
296
|
-
id_vec[1],
|
|
297
|
-
id_vec[2],
|
|
298
|
-
)
|
|
304
|
+
f"{i_angle + 1:d} {angle_type_lst[i_angle]:d} {id_vec[0]:d} {id_vec[1]:d} {id_vec[2]:d}"
|
|
299
305
|
+ "\n"
|
|
300
306
|
)
|
|
301
307
|
else:
|
|
@@ -350,7 +356,6 @@ def get_bonds(
|
|
|
350
356
|
tolerance=2,
|
|
351
357
|
id_list=None,
|
|
352
358
|
width_buffer=1.2,
|
|
353
|
-
allow_ragged=None,
|
|
354
359
|
mode="ragged",
|
|
355
360
|
norm_order=2,
|
|
356
361
|
)
|