lammpsparser 0.0.7__tar.gz → 0.0.9__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.7 → lammpsparser-0.0.9}/PKG-INFO +6 -6
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/pyproject.toml +5 -5
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/_version.py +2 -2
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/calculate.py +24 -49
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/data.py +11 -10
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/file.py +22 -19
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/output.py +4 -1
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/structure.py +48 -62
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/output.py +45 -47
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/output_raw.py +24 -24
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/potential.py +21 -25
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/structure.py +52 -70
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/units.py +15 -17
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/.gitignore +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/LICENSE +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/README.md +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/__init__.py +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/__init__.py +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/constraints.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: lammpsparser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
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
|
|
@@ -50,11 +50,11 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
50
50
|
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
|
-
Requires-Dist: numpy<=2.
|
|
54
|
-
Requires-Dist: pandas<=3.0.
|
|
55
|
-
Requires-Dist: scipy<=1.
|
|
53
|
+
Requires-Dist: numpy<=2.5.3,>=1.26.0
|
|
54
|
+
Requires-Dist: pandas<=3.0.5,>=2.0.3
|
|
55
|
+
Requires-Dist: scipy<=1.18.0,>=1.11.1
|
|
56
56
|
Provides-Extra: compatibility
|
|
57
|
-
Requires-Dist: structuretoolkit<=0.0.
|
|
57
|
+
Requires-Dist: structuretoolkit<=0.0.46,>=0.0.30; extra == 'compatibility'
|
|
58
58
|
Provides-Extra: h5md
|
|
59
59
|
Requires-Dist: h5py<=3.16.0,>=3.9.0; extra == 'h5md'
|
|
60
60
|
Description-Content-Type: text/markdown
|
|
@@ -5,7 +5,7 @@ requires = [
|
|
|
5
5
|
"h5py",
|
|
6
6
|
"pandas",
|
|
7
7
|
"scipy",
|
|
8
|
-
"hatchling==1.
|
|
8
|
+
"hatchling==1.32.0",
|
|
9
9
|
"hatch-vcs==0.5.0",
|
|
10
10
|
]
|
|
11
11
|
build-backend = "hatchling.build"
|
|
@@ -35,9 +35,9 @@ classifiers = [
|
|
|
35
35
|
]
|
|
36
36
|
dependencies = [
|
|
37
37
|
"ase>=3.22.1,<=3.29.0",
|
|
38
|
-
"numpy>=1.26.0,<=2.
|
|
39
|
-
"pandas>=2.0.3,<=3.0.
|
|
40
|
-
"scipy>=1.11.1,<=1.
|
|
38
|
+
"numpy>=1.26.0,<=2.5.3",
|
|
39
|
+
"pandas>=2.0.3,<=3.0.5",
|
|
40
|
+
"scipy>=1.11.1,<=1.18.0",
|
|
41
41
|
]
|
|
42
42
|
dynamic = ["version"]
|
|
43
43
|
|
|
@@ -48,7 +48,7 @@ Repository = "https://github.com/pyiron/lammpsparser"
|
|
|
48
48
|
|
|
49
49
|
[project.optional-dependencies]
|
|
50
50
|
h5md = ["h5py>=3.9.0,<=3.16.0"]
|
|
51
|
-
compatibility = ["structuretoolkit>=0.0.30,<=0.0.
|
|
51
|
+
compatibility = ["structuretoolkit>=0.0.30,<=0.0.46"]
|
|
52
52
|
|
|
53
53
|
[tool.hatch.build]
|
|
54
54
|
include = [
|
|
@@ -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.9'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 9)
|
|
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:
|
|
@@ -269,23 +245,22 @@ def calc_minimize(
|
|
|
269
245
|
"No rotation matrix given while trying to convert pressure. "
|
|
270
246
|
"This is most likely due to no structure being defined."
|
|
271
247
|
)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
)
|
|
248
|
+
rotation_matrix, structure = _get_rotation_matrix(
|
|
249
|
+
structure=structure, pressure=pressure
|
|
250
|
+
)
|
|
276
251
|
# force_skewed = False
|
|
277
252
|
pressure = _pressure_to_lammps(
|
|
278
253
|
pressure=pressure, rotation_matrix=rotation_matrix, units=units
|
|
279
254
|
)
|
|
280
255
|
if np.isscalar(pressure):
|
|
281
|
-
str_press = " iso {
|
|
256
|
+
str_press = f" iso {float(cast(Any, pressure))}"
|
|
282
257
|
else:
|
|
283
258
|
str_press = ""
|
|
284
259
|
for ii, (press, str_axis) in enumerate(
|
|
285
260
|
zip(pressure, ["x", "y", "z", "xy", "xz", "yz"])
|
|
286
261
|
):
|
|
287
262
|
if press is not None:
|
|
288
|
-
str_press += " {} {}"
|
|
263
|
+
str_press += f" {str_axis} {press}"
|
|
289
264
|
# if ii > 2:
|
|
290
265
|
# force_skewed = True
|
|
291
266
|
if len(str_press) > 1:
|
|
@@ -407,11 +382,11 @@ def _pressure_to_lammps(pressure, rotation_matrix, units="metal"):
|
|
|
407
382
|
In case of a single pressure value, it is again returned as a single pressure value, to be used with the "iso"
|
|
408
383
|
option (i.e., coupled deformation in x, y, and z).
|
|
409
384
|
|
|
410
|
-
Finally, we also ensure that the units are converted from
|
|
385
|
+
Finally, we also ensure that the units are converted from lammpsparser's GPa to whatever Lammps needs.
|
|
411
386
|
|
|
412
387
|
Args:
|
|
413
388
|
pressure (float/list/tuple/numpy.ndarray): The pressure(s) to convert.
|
|
414
|
-
rotation_matrix (numpy.ndarray): The 3x3 matrix rotating from the
|
|
389
|
+
rotation_matrix (numpy.ndarray): The 3x3 matrix rotating from the ASE to Lammps coordinate frame.
|
|
415
390
|
|
|
416
391
|
Returns:
|
|
417
392
|
(list): pxx, pyy, pzz, pxy, pxz, and pyz to be passed to Lammps.
|
|
@@ -433,7 +408,7 @@ def _pressure_to_lammps(pressure, rotation_matrix, units="metal"):
|
|
|
433
408
|
if len(pressure) > 6:
|
|
434
409
|
raise ValueError(
|
|
435
410
|
"Pressure can have a maximum of 6 values, (x, y, z, xy, xz, and yz), but got "
|
|
436
|
-
+ "{
|
|
411
|
+
+ f"{len(pressure)}"
|
|
437
412
|
)
|
|
438
413
|
pressure = [float(p) if p is not None else None for p in pressure]
|
|
439
414
|
pressure += (6 - len(pressure)) * [None]
|
|
@@ -451,7 +426,7 @@ def _pressure_to_lammps(pressure, rotation_matrix, units="metal"):
|
|
|
451
426
|
"Cells which are not orthorhombic or an upper-triangular cell are incompatible with Lammps "
|
|
452
427
|
"constant pressure calculations unless the entire pressure tensor is defined. "
|
|
453
428
|
"The reason is that Lammps demands such cells be represented with an "
|
|
454
|
-
"upper-triangular unit cell, thus a rotation between Lammps and
|
|
429
|
+
"upper-triangular unit cell, thus a rotation between Lammps and ASE coordinate "
|
|
455
430
|
"frames is required; it is not possible to rotate the pressure tensor if any of "
|
|
456
431
|
"its components is None."
|
|
457
432
|
)
|
|
@@ -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,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
4
|
+
import pathlib
|
|
2
5
|
import shutil
|
|
3
6
|
import subprocess
|
|
4
7
|
from dataclasses import asdict
|
|
5
|
-
from typing import Optional, Union
|
|
6
8
|
|
|
7
9
|
import pandas
|
|
8
10
|
from ase.atoms import Atoms
|
|
@@ -24,27 +26,27 @@ def lammps_file_interface_function(
|
|
|
24
26
|
structure: Atoms,
|
|
25
27
|
potential: str,
|
|
26
28
|
calc_mode: str = "static",
|
|
27
|
-
calc_kwargs:
|
|
28
|
-
calc_dataclass:
|
|
29
|
+
calc_kwargs: dict | None = None,
|
|
30
|
+
calc_dataclass: CalcMDInput | CalcMinimizeInput | None = None,
|
|
29
31
|
units: str = "metal",
|
|
30
|
-
lmp_command:
|
|
31
|
-
resource_path:
|
|
32
|
-
input_control_file:
|
|
32
|
+
lmp_command: str | None = None,
|
|
33
|
+
resource_path: str | None = None,
|
|
34
|
+
input_control_file: dict | None = None,
|
|
33
35
|
write_restart_file: bool = False,
|
|
34
36
|
read_restart_file: bool = False,
|
|
35
37
|
restart_file: str = "restart.out",
|
|
36
38
|
dump_final_structure: bool = False,
|
|
37
39
|
):
|
|
38
40
|
"""
|
|
39
|
-
A single function to execute a LAMMPS calculation based on the LAMMPS job implemented in
|
|
41
|
+
A single function to execute a LAMMPS calculation based on the LAMMPS job interface implemented in lammpsparser
|
|
40
42
|
|
|
41
43
|
Examples:
|
|
42
44
|
|
|
43
45
|
>>> import os
|
|
44
46
|
>>> from ase.build import bulk
|
|
45
|
-
>>> from
|
|
47
|
+
>>> from lammpsparser.compatibility.file import lammps_file_interface_function
|
|
46
48
|
>>>
|
|
47
|
-
>>> shell_output, parsed_output, job_crashed =
|
|
49
|
+
>>> shell_output, parsed_output, job_crashed = lammps_file_interface_function(
|
|
48
50
|
... working_directory=os.path.abspath("lmp_working_directory"),
|
|
49
51
|
... structure=bulk("Al", cubic=True),
|
|
50
52
|
... potential='2009--Mendelev-M-I--Al-Mg--LAMMPS--ipr1',
|
|
@@ -119,7 +121,7 @@ def lammps_file_interface_function(
|
|
|
119
121
|
+ " -in lmp.in"
|
|
120
122
|
)
|
|
121
123
|
|
|
122
|
-
|
|
124
|
+
pathlib.Path(working_directory).mkdir(exist_ok=True, parents=True)
|
|
123
125
|
potential_lst, potential_replace, species = _get_potential(
|
|
124
126
|
potential=potential, resource_path=resource_path
|
|
125
127
|
)
|
|
@@ -178,7 +180,7 @@ def lammps_file_interface_function(
|
|
|
178
180
|
structure=structure, calc_md=True
|
|
179
181
|
).items()
|
|
180
182
|
]
|
|
181
|
-
if "n_ionic_steps" in calc_kwargs
|
|
183
|
+
if "n_ionic_steps" in calc_kwargs:
|
|
182
184
|
n_ionic_steps = int(calc_kwargs.pop("n_ionic_steps"))
|
|
183
185
|
else:
|
|
184
186
|
n_ionic_steps = 1
|
|
@@ -192,7 +194,7 @@ def lammps_file_interface_function(
|
|
|
192
194
|
)
|
|
193
195
|
if read_restart_file:
|
|
194
196
|
lmp_str_lst += ["reset_timestep 0"]
|
|
195
|
-
lmp_str_lst += ["run {} "
|
|
197
|
+
lmp_str_lst += [f"run {n_ionic_steps} "]
|
|
196
198
|
last_step_is_regular_dump = n_ionic_steps % calc_kwargs.get("n_print", 1) == 0
|
|
197
199
|
if dump_final_structure and not last_step_is_regular_dump:
|
|
198
200
|
lmp_str_lst += [
|
|
@@ -232,7 +234,7 @@ def lammps_file_interface_function(
|
|
|
232
234
|
if write_restart_file:
|
|
233
235
|
lmp_str_lst.append(f"write_restart {os.path.basename(restart_file)}")
|
|
234
236
|
|
|
235
|
-
with
|
|
237
|
+
with pathlib.Path(os.path.join(working_directory, "lmp.in")).open("w") as f:
|
|
236
238
|
f.writelines([line + "\n" for line in lmp_str_lst])
|
|
237
239
|
|
|
238
240
|
write_lammps_datafile(
|
|
@@ -305,8 +307,8 @@ def lammps_file_initialization(
|
|
|
305
307
|
|
|
306
308
|
|
|
307
309
|
def _modify_input_dict(
|
|
308
|
-
input_control_file:
|
|
309
|
-
lmp_str_lst: list[str] =
|
|
310
|
+
input_control_file: dict | None = None,
|
|
311
|
+
lmp_str_lst: list[str] | None = None,
|
|
310
312
|
):
|
|
311
313
|
"""
|
|
312
314
|
Apply user-supplied overrides to a LAMMPS input line list.
|
|
@@ -325,13 +327,15 @@ def _modify_input_dict(
|
|
|
325
327
|
Returns:
|
|
326
328
|
list[str]: Modified (or unchanged) list of LAMMPS input lines.
|
|
327
329
|
"""
|
|
330
|
+
if lmp_str_lst is None:
|
|
331
|
+
lmp_str_lst = []
|
|
328
332
|
if input_control_file is not None:
|
|
329
333
|
lmp_tmp_lst, keys_used = [], []
|
|
330
334
|
for line in lmp_str_lst:
|
|
331
335
|
ls = line.split()
|
|
332
336
|
if len(ls) >= 1: # Remove empty lines
|
|
333
337
|
key = ls[0]
|
|
334
|
-
if key in input_control_file
|
|
338
|
+
if key in input_control_file:
|
|
335
339
|
lmp_tmp_lst.append(key + " " + input_control_file[key])
|
|
336
340
|
keys_used.append(key)
|
|
337
341
|
else:
|
|
@@ -341,11 +345,10 @@ def _modify_input_dict(
|
|
|
341
345
|
lmp_tmp_lst.append(k + " " + v)
|
|
342
346
|
|
|
343
347
|
return lmp_tmp_lst
|
|
344
|
-
|
|
345
|
-
return lmp_str_lst
|
|
348
|
+
return lmp_str_lst
|
|
346
349
|
|
|
347
350
|
|
|
348
|
-
def _get_potential(potential, resource_path:
|
|
351
|
+
def _get_potential(potential, resource_path: str | None = None):
|
|
349
352
|
"""
|
|
350
353
|
Resolve a potential specification to LAMMPS input lines and species list.
|
|
351
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:
|
|
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
|
|
|
@@ -49,14 +51,14 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
49
51
|
self._string_input = input_str + self._get_velocities_input_string()
|
|
50
52
|
|
|
51
53
|
@property
|
|
52
|
-
def molecule_ids(self) ->
|
|
54
|
+
def molecule_ids(self) -> list[int] | np.ndarray:
|
|
53
55
|
"""Per-atom molecule IDs; defaults to all atoms in a single molecule."""
|
|
54
56
|
if self._molecule_ids is None or len(self._molecule_ids) == 0:
|
|
55
57
|
return np.ones(len(cast(Atoms, self._structure)), dtype=int)
|
|
56
58
|
return self._molecule_ids
|
|
57
59
|
|
|
58
60
|
@molecule_ids.setter
|
|
59
|
-
def molecule_ids(self, molecule_ids:
|
|
61
|
+
def molecule_ids(self, molecule_ids: list[int] | None):
|
|
60
62
|
self._molecule_ids = molecule_ids
|
|
61
63
|
|
|
62
64
|
def structure_bond(self):
|
|
@@ -79,7 +81,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
79
81
|
|
|
80
82
|
elements = self._structure.get_chemical_symbols()
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
# Standard atoms stuff
|
|
83
85
|
atoms = "Atoms \n\n"
|
|
84
86
|
# atom_style bond
|
|
85
87
|
# format: atom-ID, molecule-ID, atom_type, x, y, z
|
|
@@ -115,7 +117,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
115
117
|
else:
|
|
116
118
|
raise ValueError("dimension 1 not yet implemented")
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
# Bond related.
|
|
119
121
|
# This seems independent from the lammps atom type ids, because bonds only use atom ids
|
|
120
122
|
el_list = set(self._structure.get_chemical_symbols())
|
|
121
123
|
el_dict = OrderedDict()
|
|
@@ -154,9 +156,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
154
156
|
|
|
155
157
|
bonds_str = "Bonds \n\n"
|
|
156
158
|
for i_bond, (i_a, i_b, b_type) in enumerate(bonds):
|
|
157
|
-
bonds_str +=
|
|
158
|
-
"{0:d} {1:d} {2:d} {3:d}".format(i_bond + 1, b_type, i_a, i_b) + "\n"
|
|
159
|
-
)
|
|
159
|
+
bonds_str += f"{i_bond + 1:d} {b_type:d} {i_a:d} {i_b:d}" + "\n"
|
|
160
160
|
|
|
161
161
|
return (
|
|
162
162
|
self.lammps_header(
|
|
@@ -199,17 +199,14 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
199
199
|
for species_name in set(self.structure.get_chemical_symbols())
|
|
200
200
|
}
|
|
201
201
|
else:
|
|
202
|
-
q_dict =
|
|
203
|
-
species_name: 0.0
|
|
204
|
-
for species_name in set(self.structure.get_chemical_symbols())
|
|
205
|
-
}
|
|
202
|
+
q_dict = dict.fromkeys(set(self.structure.get_chemical_symbols()), 0.0)
|
|
206
203
|
|
|
207
204
|
bonds_lst, angles_lst = [], []
|
|
208
205
|
bond_type_lst, angle_type_lst = [], []
|
|
209
206
|
# Using a cutoff distance to draw the bonds instead of the number of neighbors
|
|
210
207
|
# Only if any bonds are defined
|
|
211
208
|
if self._bond_dict is not None and len(self._bond_dict.keys()) > 0:
|
|
212
|
-
cutoff_list =
|
|
209
|
+
cutoff_list = []
|
|
213
210
|
for val in self._bond_dict.values():
|
|
214
211
|
cutoff_list.append(np.max(val["cutoff_list"]))
|
|
215
212
|
max_cutoff = np.max(cutoff_list)
|
|
@@ -222,45 +219,42 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
222
219
|
# Go through all elements for which bonds are defined
|
|
223
220
|
for element, val in self._bond_dict.items():
|
|
224
221
|
el_1_list = select_index(structure=self._structure, element=element)
|
|
225
|
-
if el_1_list is not None:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
222
|
+
if el_1_list is not None and len(el_1_list) > 0:
|
|
223
|
+
for i, v in enumerate(val["element_list"]):
|
|
224
|
+
el_2_list = select_index(structure=self._structure, element=v)
|
|
225
|
+
cutoff_dist = val["cutoff_list"][i]
|
|
226
|
+
for j, ind in enumerate(
|
|
227
|
+
np.array(neighbors.indices, dtype=object)[el_1_list]
|
|
228
|
+
):
|
|
229
|
+
# Only chose those indices within the cutoff distance and which belong
|
|
230
|
+
# to the species defined in the element_list
|
|
231
|
+
# i is the index of each bond type, and j is the element index
|
|
232
|
+
id_el = el_1_list[j]
|
|
233
|
+
bool_1 = (
|
|
234
|
+
np.array(neighbors.distances, dtype=object)[id_el]
|
|
235
|
+
<= cutoff_dist
|
|
230
236
|
)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
237
|
+
act_ind = ind[bool_1]
|
|
238
|
+
bool_2 = np.isin(act_ind, el_2_list)
|
|
239
|
+
final_ind = act_ind[bool_2]
|
|
240
|
+
# Get the bond and angle type
|
|
241
|
+
bond_type = val["bond_type_list"][i]
|
|
242
|
+
angle_type = val["angle_type_list"][i]
|
|
243
|
+
# Draw only maximum allowed bonds
|
|
244
|
+
final_ind = final_ind[: val["max_bond_list"][i]]
|
|
245
|
+
for fi in final_ind:
|
|
246
|
+
bonds_lst.append([id_el + 1, fi + 1])
|
|
247
|
+
bond_type_lst.append(bond_type)
|
|
248
|
+
# Draw angles if at least 2 bonds are present and if an angle type is defined for this
|
|
249
|
+
# particular set of bonds
|
|
250
|
+
if (
|
|
251
|
+
len(final_ind) >= 2
|
|
252
|
+
and val["angle_type_list"][i] is not None
|
|
234
253
|
):
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
# i is the index of each bond type, and j is the element index
|
|
238
|
-
id_el = el_1_list[j]
|
|
239
|
-
bool_1 = (
|
|
240
|
-
np.array(neighbors.distances, dtype=object)[id_el]
|
|
241
|
-
<= cutoff_dist
|
|
254
|
+
angles_lst.append(
|
|
255
|
+
[final_ind[0] + 1, id_el + 1, final_ind[1] + 1]
|
|
242
256
|
)
|
|
243
|
-
|
|
244
|
-
bool_2 = np.isin(act_ind, el_2_list)
|
|
245
|
-
final_ind = act_ind[bool_2]
|
|
246
|
-
# Get the bond and angle type
|
|
247
|
-
bond_type = val["bond_type_list"][i]
|
|
248
|
-
angle_type = val["angle_type_list"][i]
|
|
249
|
-
# Draw only maximum allowed bonds
|
|
250
|
-
final_ind = final_ind[: val["max_bond_list"][i]]
|
|
251
|
-
for fi in final_ind:
|
|
252
|
-
bonds_lst.append([id_el + 1, fi + 1])
|
|
253
|
-
bond_type_lst.append(bond_type)
|
|
254
|
-
# Draw angles if at least 2 bonds are present and if an angle type is defined for this
|
|
255
|
-
# particular set of bonds
|
|
256
|
-
if (
|
|
257
|
-
len(final_ind) >= 2
|
|
258
|
-
and val["angle_type_list"][i] is not None
|
|
259
|
-
):
|
|
260
|
-
angles_lst.append(
|
|
261
|
-
[final_ind[0] + 1, id_el + 1, final_ind[1] + 1]
|
|
262
|
-
)
|
|
263
|
-
angle_type_lst.append(angle_type)
|
|
257
|
+
angle_type_lst.append(angle_type)
|
|
264
258
|
|
|
265
259
|
if len(bond_type_lst) == 0:
|
|
266
260
|
num_bond_types = 0
|
|
@@ -294,9 +288,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
294
288
|
bonds_str = "Bonds \n\n"
|
|
295
289
|
for i_bond, id_vec in enumerate(bonds_lst):
|
|
296
290
|
bonds_str += (
|
|
297
|
-
"{
|
|
298
|
-
i_bond + 1, bond_type_lst[i_bond], id_vec[0], id_vec[1]
|
|
299
|
-
)
|
|
291
|
+
f"{i_bond + 1:d} {bond_type_lst[i_bond]:d} {id_vec[0]:d} {id_vec[1]:d}"
|
|
300
292
|
+ "\n"
|
|
301
293
|
)
|
|
302
294
|
else:
|
|
@@ -306,13 +298,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
306
298
|
angles_str = "Angles \n\n"
|
|
307
299
|
for i_angle, id_vec in enumerate(angles_lst):
|
|
308
300
|
angles_str += (
|
|
309
|
-
"{
|
|
310
|
-
i_angle + 1,
|
|
311
|
-
angle_type_lst[i_angle],
|
|
312
|
-
id_vec[0],
|
|
313
|
-
id_vec[1],
|
|
314
|
-
id_vec[2],
|
|
315
|
-
)
|
|
301
|
+
f"{i_angle + 1:d} {angle_type_lst[i_angle]:d} {id_vec[0]:d} {id_vec[1]:d} {id_vec[2]:d}"
|
|
316
302
|
+ "\n"
|
|
317
303
|
)
|
|
318
304
|
else:
|