lammpsparser 0.0.7__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.7 → lammpsparser-0.0.8}/PKG-INFO +2 -2
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/pyproject.toml +2 -2
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/_version.py +2 -2
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/calculate.py +21 -45
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/data.py +11 -10
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/file.py +18 -15
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/output.py +4 -1
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/structure.py +45 -56
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/output.py +36 -38
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/output_raw.py +21 -22
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/potential.py +8 -9
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/structure.py +50 -69
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/units.py +10 -11
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/.gitignore +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/LICENSE +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/README.md +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/__init__.py +0 -0
- {lammpsparser-0.0.7 → lammpsparser-0.0.8}/src/lammpsparser/compatibility/__init__.py +0 -0
- {lammpsparser-0.0.7 → 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,27 +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",
|
|
36
37
|
dump_final_structure: bool = False,
|
|
37
38
|
):
|
|
38
39
|
"""
|
|
39
|
-
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
|
|
40
41
|
|
|
41
42
|
Examples:
|
|
42
43
|
|
|
43
44
|
>>> import os
|
|
44
45
|
>>> from ase.build import bulk
|
|
45
|
-
>>> from
|
|
46
|
+
>>> from lammpsparser.compatibility.file import lammps_file_interface_function
|
|
46
47
|
>>>
|
|
47
|
-
>>> shell_output, parsed_output, job_crashed =
|
|
48
|
+
>>> shell_output, parsed_output, job_crashed = lammps_file_interface_function(
|
|
48
49
|
... working_directory=os.path.abspath("lmp_working_directory"),
|
|
49
50
|
... structure=bulk("Al", cubic=True),
|
|
50
51
|
... potential='2009--Mendelev-M-I--Al-Mg--LAMMPS--ipr1',
|
|
@@ -178,7 +179,7 @@ def lammps_file_interface_function(
|
|
|
178
179
|
structure=structure, calc_md=True
|
|
179
180
|
).items()
|
|
180
181
|
]
|
|
181
|
-
if "n_ionic_steps" in calc_kwargs
|
|
182
|
+
if "n_ionic_steps" in calc_kwargs:
|
|
182
183
|
n_ionic_steps = int(calc_kwargs.pop("n_ionic_steps"))
|
|
183
184
|
else:
|
|
184
185
|
n_ionic_steps = 1
|
|
@@ -192,7 +193,7 @@ def lammps_file_interface_function(
|
|
|
192
193
|
)
|
|
193
194
|
if read_restart_file:
|
|
194
195
|
lmp_str_lst += ["reset_timestep 0"]
|
|
195
|
-
lmp_str_lst += ["run {} "
|
|
196
|
+
lmp_str_lst += [f"run {n_ionic_steps} "]
|
|
196
197
|
last_step_is_regular_dump = n_ionic_steps % calc_kwargs.get("n_print", 1) == 0
|
|
197
198
|
if dump_final_structure and not last_step_is_regular_dump:
|
|
198
199
|
lmp_str_lst += [
|
|
@@ -305,8 +306,8 @@ def lammps_file_initialization(
|
|
|
305
306
|
|
|
306
307
|
|
|
307
308
|
def _modify_input_dict(
|
|
308
|
-
input_control_file:
|
|
309
|
-
lmp_str_lst: list[str] =
|
|
309
|
+
input_control_file: dict | None = None,
|
|
310
|
+
lmp_str_lst: list[str] | None = None,
|
|
310
311
|
):
|
|
311
312
|
"""
|
|
312
313
|
Apply user-supplied overrides to a LAMMPS input line list.
|
|
@@ -325,13 +326,15 @@ def _modify_input_dict(
|
|
|
325
326
|
Returns:
|
|
326
327
|
list[str]: Modified (or unchanged) list of LAMMPS input lines.
|
|
327
328
|
"""
|
|
329
|
+
if lmp_str_lst is None:
|
|
330
|
+
lmp_str_lst = []
|
|
328
331
|
if input_control_file is not None:
|
|
329
332
|
lmp_tmp_lst, keys_used = [], []
|
|
330
333
|
for line in lmp_str_lst:
|
|
331
334
|
ls = line.split()
|
|
332
335
|
if len(ls) >= 1: # Remove empty lines
|
|
333
336
|
key = ls[0]
|
|
334
|
-
if key in input_control_file
|
|
337
|
+
if key in input_control_file:
|
|
335
338
|
lmp_tmp_lst.append(key + " " + input_control_file[key])
|
|
336
339
|
keys_used.append(key)
|
|
337
340
|
else:
|
|
@@ -345,7 +348,7 @@ def _modify_input_dict(
|
|
|
345
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):
|
|
@@ -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(
|
|
@@ -209,7 +209,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
209
209
|
# Using a cutoff distance to draw the bonds instead of the number of neighbors
|
|
210
210
|
# Only if any bonds are defined
|
|
211
211
|
if self._bond_dict is not None and len(self._bond_dict.keys()) > 0:
|
|
212
|
-
cutoff_list =
|
|
212
|
+
cutoff_list = []
|
|
213
213
|
for val in self._bond_dict.values():
|
|
214
214
|
cutoff_list.append(np.max(val["cutoff_list"]))
|
|
215
215
|
max_cutoff = np.max(cutoff_list)
|
|
@@ -222,45 +222,42 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
222
222
|
# Go through all elements for which bonds are defined
|
|
223
223
|
for element, val in self._bond_dict.items():
|
|
224
224
|
el_1_list = select_index(structure=self._structure, element=element)
|
|
225
|
-
if el_1_list is not None:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
230
239
|
)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
234
256
|
):
|
|
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
|
|
257
|
+
angles_lst.append(
|
|
258
|
+
[final_ind[0] + 1, id_el + 1, final_ind[1] + 1]
|
|
242
259
|
)
|
|
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)
|
|
260
|
+
angle_type_lst.append(angle_type)
|
|
264
261
|
|
|
265
262
|
if len(bond_type_lst) == 0:
|
|
266
263
|
num_bond_types = 0
|
|
@@ -294,9 +291,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
294
291
|
bonds_str = "Bonds \n\n"
|
|
295
292
|
for i_bond, id_vec in enumerate(bonds_lst):
|
|
296
293
|
bonds_str += (
|
|
297
|
-
"{
|
|
298
|
-
i_bond + 1, bond_type_lst[i_bond], id_vec[0], id_vec[1]
|
|
299
|
-
)
|
|
294
|
+
f"{i_bond + 1:d} {bond_type_lst[i_bond]:d} {id_vec[0]:d} {id_vec[1]:d}"
|
|
300
295
|
+ "\n"
|
|
301
296
|
)
|
|
302
297
|
else:
|
|
@@ -306,13 +301,7 @@ class LammpsStructureCompatibility(LammpsStructure):
|
|
|
306
301
|
angles_str = "Angles \n\n"
|
|
307
302
|
for i_angle, id_vec in enumerate(angles_lst):
|
|
308
303
|
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
|
-
)
|
|
304
|
+
f"{i_angle + 1:d} {angle_type_lst[i_angle]:d} {id_vec[0]:d} {id_vec[1]:d} {id_vec[2]:d}"
|
|
316
305
|
+ "\n"
|
|
317
306
|
)
|
|
318
307
|
else:
|