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.
Files changed (19) hide show
  1. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/PKG-INFO +6 -6
  2. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/pyproject.toml +5 -5
  3. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/_version.py +2 -2
  4. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/calculate.py +24 -49
  5. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/data.py +11 -10
  6. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/file.py +22 -19
  7. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/output.py +4 -1
  8. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/structure.py +48 -62
  9. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/output.py +45 -47
  10. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/output_raw.py +24 -24
  11. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/potential.py +21 -25
  12. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/structure.py +52 -70
  13. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/units.py +15 -17
  14. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/.gitignore +0 -0
  15. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/LICENSE +0 -0
  16. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/README.md +0 -0
  17. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/__init__.py +0 -0
  18. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/__init__.py +0 -0
  19. {lammpsparser-0.0.7 → lammpsparser-0.0.9}/src/lammpsparser/compatibility/constraints.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: lammpsparser
3
- Version: 0.0.7
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.4.6,>=1.26.0
54
- Requires-Dist: pandas<=3.0.3,>=2.0.3
55
- Requires-Dist: scipy<=1.17.1,>=1.11.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.45,>=0.0.30; extra == 'compatibility'
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.30.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.4.6",
39
- "pandas>=2.0.3,<=3.0.3",
40
- "scipy>=1.11.1,<=1.17.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.45"]
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.7'
22
- __version_tuple__ = version_tuple = (0, 0, 7)
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 pyiron to Lammps coordinate
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.keys():
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 {}".format(len(temperature))
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 {0} {0} {1}".format(
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 += " {0} {1} {1} {2}".format(
141
- coord, value, pressure_damping_timescale
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} {2} {3} zero yes".format(
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} {2}".format(
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} {2} {3} zero yes".format(
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} {2}".format(
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 {} ".format(n_print))
194
- line_lst.append("timestep {}".format(time_step))
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 pyiron to Lammps coordinate frame.
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.keys():
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 {} ".format(n_print)]
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
- else:
273
- rotation_matrix, structure = _get_rotation_matrix(
274
- structure=structure, pressure=pressure
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 {}".format(float(cast(Any, pressure)))
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 += " {} {}".format(str_axis, 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 pyiron's GPa to whatever Lammps needs.
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 pyiron to Lammps coordinate frame.
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
- + "{}".format(len(pressure))
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 pyiron coordinate "
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: Optional[Union[float, list]] = None
45
- pressure: Optional[Union[float, list, np.ndarray]] = None
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: Optional[int] = None
53
- initial_temperature: Optional[float] = None
53
+ tloop: int | None = None
54
+ initial_temperature: float | None = None
54
55
  langevin: bool = False
55
- delta_temp: Optional[float] = None
56
- delta_press: Optional[float] = None
57
- rotation_matrix: Optional[Union[list, np.ndarray]] = None
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: Optional[Union[float, list, np.ndarray]] = None
85
+ pressure: float | list | np.ndarray | None = None
85
86
  n_print: int = 1
86
87
  style: str = "cg"
87
- rotation_matrix: Optional[Union[list, np.ndarray]] = None
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: Optional[dict] = None,
28
- calc_dataclass: Optional[Union[CalcMDInput, CalcMinimizeInput]] = None,
29
+ calc_kwargs: dict | None = None,
30
+ calc_dataclass: CalcMDInput | CalcMinimizeInput | None = None,
29
31
  units: str = "metal",
30
- lmp_command: Optional[str] = None,
31
- resource_path: Optional[str] = None,
32
- input_control_file: Optional[dict] = None,
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 pyiron
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 pyiron_atomistics.lammps.lammps import lammps_function
47
+ >>> from lammpsparser.compatibility.file import lammps_file_interface_function
46
48
  >>>
47
- >>> shell_output, parsed_output, job_crashed = lammps_function(
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
- os.makedirs(working_directory, exist_ok=True)
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.keys():
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 {} ".format(n_ionic_steps)]
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 open(os.path.join(working_directory, "lmp.in"), "w") as f:
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: Optional[dict] = None,
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.keys():
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
- else:
345
- return lmp_str_lst
348
+ return lmp_str_lst
346
349
 
347
350
 
348
- def _get_potential(potential, resource_path: Optional[str] = None):
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 Dict, List, Optional, Union, cast
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: Optional[Dict] = None,
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: Optional[List[int]] = []
22
+ self._molecule_ids: list[int] | None = []
21
23
 
22
24
  @property
23
- def structure(self) -> Optional[Atoms]:
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) -> Union[List[int], np.ndarray]:
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: Optional[List[int]]):
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
- ## Standard atoms stuff
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
- ## Bond related.
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 = 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
- if len(el_1_list) > 0:
227
- for i, v in enumerate(val["element_list"]):
228
- el_2_list = select_index(
229
- structure=self._structure, element=v
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
- cutoff_dist = val["cutoff_list"][i]
232
- for j, ind in enumerate(
233
- np.array(neighbors.indices, dtype=object)[el_1_list]
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
- # Only chose those indices within the cutoff distance and which belong
236
- # to the species defined in the element_list
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
- act_ind = ind[bool_1]
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
- "{0:d} {1:d} {2:d} {3:d}".format(
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
- "{0:d} {1:d} {2:d} {3:d} {4:d}".format(
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: