lammpsparser 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lammpsparser/__init__.py +38 -0
- lammpsparser/_version.py +34 -0
- lammpsparser/compatibility/__init__.py +0 -0
- lammpsparser/compatibility/calculate.py +484 -0
- lammpsparser/compatibility/constraints.py +106 -0
- lammpsparser/compatibility/file.py +285 -0
- lammpsparser/compatibility/structure.py +336 -0
- lammpsparser/output.py +340 -0
- lammpsparser/output_raw.py +251 -0
- lammpsparser/potential.py +380 -0
- lammpsparser/structure.py +536 -0
- lammpsparser/units.py +246 -0
- lammpsparser-0.0.1.dist-info/METADATA +146 -0
- lammpsparser-0.0.1.dist-info/RECORD +16 -0
- lammpsparser-0.0.1.dist-info/WHEEL +4 -0
- lammpsparser-0.0.1.dist-info/licenses/LICENSE +29 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
import pandas
|
|
6
|
+
from ase.atoms import Atoms
|
|
7
|
+
|
|
8
|
+
potential_installation = """
|
|
9
|
+
Potential installation guide:
|
|
10
|
+
|
|
11
|
+
1. Check whether iprpy-data is installed. If not, install it using:
|
|
12
|
+
|
|
13
|
+
`conda install -c conda-forge iprpy-data`
|
|
14
|
+
|
|
15
|
+
2. Check whether the resource path is set via:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import os
|
|
19
|
+
print(os.environ["CONDA_PREFIX"])
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
3. If the resource path is set, you can call the potential using:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from atomistics.calculators import get_potential_by_name
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
get_potential_by_name(
|
|
29
|
+
potential_name=my_potential,
|
|
30
|
+
resource_path=os.path.join(os.environ["CONDA_PREFIX"], "share", "iprpy"),
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PotentialAbstract:
|
|
38
|
+
"""
|
|
39
|
+
The PotentialAbstract class loads a list of available potentials and sorts them. Afterwards the potentials can be
|
|
40
|
+
accessed through:
|
|
41
|
+
PotentialAbstract.<Element>.<Element> or PotentialAbstract.find_potentials_set({<Element>, <Element>}
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
potential_df:
|
|
45
|
+
default_df:
|
|
46
|
+
selected_atoms:
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
potential_df: pandas.DataFrame,
|
|
52
|
+
default_df: pandas.DataFrame = None,
|
|
53
|
+
selected_atoms: list[str] = None,
|
|
54
|
+
):
|
|
55
|
+
self._potential_df = potential_df
|
|
56
|
+
self._default_df = default_df
|
|
57
|
+
if selected_atoms is not None:
|
|
58
|
+
self._selected_atoms = selected_atoms
|
|
59
|
+
else:
|
|
60
|
+
self._selected_atoms = []
|
|
61
|
+
|
|
62
|
+
def find(self, element: Union[set[str], list[str], str]) -> pandas.DataFrame:
|
|
63
|
+
"""
|
|
64
|
+
Find the potentials
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
element (set, str): element or set of elements for which you want the possible LAMMPS potentials
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
list: of possible potentials for the element or the combination of elements
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
if isinstance(element, list):
|
|
74
|
+
element = set(element)
|
|
75
|
+
elif isinstance(element, str):
|
|
76
|
+
element = {element}
|
|
77
|
+
elif not isinstance(element, set):
|
|
78
|
+
raise TypeError("Only, str, list and set supported!")
|
|
79
|
+
return self._potential_df[
|
|
80
|
+
[
|
|
81
|
+
bool(set(element).issubset(species))
|
|
82
|
+
for species in self._potential_df["Species"].values
|
|
83
|
+
]
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
def find_by_name(self, potential_name: str) -> pandas.DataFrame:
|
|
87
|
+
mask = self._potential_df["Name"] == potential_name
|
|
88
|
+
if not mask.any():
|
|
89
|
+
raise ValueError(f"Potential '{potential_name}' not found in database.")
|
|
90
|
+
return self._potential_df[mask]
|
|
91
|
+
|
|
92
|
+
def list(self) -> pandas.DataFrame:
|
|
93
|
+
"""
|
|
94
|
+
List the available potentials
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
list: of possible potentials for the element or the combination of elements
|
|
98
|
+
"""
|
|
99
|
+
return self._potential_df
|
|
100
|
+
|
|
101
|
+
def __getattr__(self, item):
|
|
102
|
+
return self[item]
|
|
103
|
+
|
|
104
|
+
def __getitem__(self, item):
|
|
105
|
+
potential_df = self.find(element=item)
|
|
106
|
+
selected_atoms = self._selected_atoms + [item]
|
|
107
|
+
return PotentialAbstract(
|
|
108
|
+
potential_df=potential_df,
|
|
109
|
+
default_df=self._default_df,
|
|
110
|
+
selected_atoms=selected_atoms,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def __str__(self) -> str:
|
|
114
|
+
return str(self.list())
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _get_potential_df(file_name_lst, resource_path):
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
file_name_lst (set):
|
|
122
|
+
resource_path (str):
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
pandas.DataFrame:
|
|
126
|
+
"""
|
|
127
|
+
for path, _folder_lst, file_lst in os.walk(resource_path):
|
|
128
|
+
for periodic_table_file_name in file_name_lst:
|
|
129
|
+
if (
|
|
130
|
+
periodic_table_file_name in file_lst
|
|
131
|
+
and periodic_table_file_name.endswith(".csv")
|
|
132
|
+
):
|
|
133
|
+
return pandas.read_csv(
|
|
134
|
+
os.path.join(path, periodic_table_file_name),
|
|
135
|
+
index_col=0,
|
|
136
|
+
converters={
|
|
137
|
+
"Species": (
|
|
138
|
+
lambda x: x.replace("'", "").strip("[]").split(", ")
|
|
139
|
+
),
|
|
140
|
+
"Config": (
|
|
141
|
+
lambda x: x.replace("'", "")
|
|
142
|
+
.replace("\\n", "\n")
|
|
143
|
+
.strip("[]")
|
|
144
|
+
.split(", ")
|
|
145
|
+
),
|
|
146
|
+
"Filename": (
|
|
147
|
+
lambda x: x.replace("'", "").strip("[]").split(", ")
|
|
148
|
+
),
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
raise ValueError(
|
|
152
|
+
"Was not able to locate the potential files." + potential_installation
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class LammpsPotentialFile(PotentialAbstract):
|
|
157
|
+
"""
|
|
158
|
+
The Potential class is derived from the PotentialAbstract class, but instead of loading the potentials from a list,
|
|
159
|
+
the potentials are loaded from a file.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
potential_df:
|
|
163
|
+
default_df:
|
|
164
|
+
selected_atoms:
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
potential_df=None,
|
|
170
|
+
default_df=None,
|
|
171
|
+
selected_atoms=None,
|
|
172
|
+
resource_path=None,
|
|
173
|
+
):
|
|
174
|
+
if potential_df is None:
|
|
175
|
+
potential_df = self._get_potential_df(
|
|
176
|
+
file_name_lst={"potentials_lammps.csv"},
|
|
177
|
+
resource_path=resource_path,
|
|
178
|
+
)
|
|
179
|
+
super().__init__(
|
|
180
|
+
potential_df=potential_df,
|
|
181
|
+
default_df=default_df,
|
|
182
|
+
selected_atoms=selected_atoms,
|
|
183
|
+
)
|
|
184
|
+
self._resource_path = resource_path
|
|
185
|
+
|
|
186
|
+
def default(self):
|
|
187
|
+
if self._default_df is not None:
|
|
188
|
+
atoms_str = "_".join(sorted(self._selected_atoms))
|
|
189
|
+
return self._default_df[
|
|
190
|
+
(self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
|
|
191
|
+
]
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
def find_default(
|
|
195
|
+
self, element: Union[set[str], list[str], str]
|
|
196
|
+
) -> pandas.DataFrame:
|
|
197
|
+
"""
|
|
198
|
+
Find the potentials
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
element (set, str): element or set of elements for which you want the possible LAMMPS potentials
|
|
202
|
+
path (bool): choose whether to return the full path to the potential or just the potential name
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
list: of possible potentials for the element or the combination of elements
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
if isinstance(element, list):
|
|
209
|
+
element = set(element)
|
|
210
|
+
elif isinstance(element, str):
|
|
211
|
+
element = {element}
|
|
212
|
+
elif not isinstance(element, set):
|
|
213
|
+
raise TypeError("Only, str, list and set supported!")
|
|
214
|
+
element_lst = list(element)
|
|
215
|
+
if self._default_df is not None:
|
|
216
|
+
merged_lst = list(set(self._selected_atoms + element_lst))
|
|
217
|
+
atoms_str = "_".join(sorted(merged_lst))
|
|
218
|
+
return self._default_df[
|
|
219
|
+
(self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
|
|
220
|
+
]
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
def __getitem__(self, item):
|
|
224
|
+
potential_df = self.find(element=item)
|
|
225
|
+
selected_atoms = self._selected_atoms + [item]
|
|
226
|
+
return LammpsPotentialFile(
|
|
227
|
+
potential_df=potential_df,
|
|
228
|
+
default_df=self._default_df,
|
|
229
|
+
selected_atoms=selected_atoms,
|
|
230
|
+
resource_path=self._resource_path,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class PotentialAvailable:
|
|
235
|
+
def __init__(self, list_of_potentials):
|
|
236
|
+
self._list_of_potentials = {
|
|
237
|
+
"pot_" + v.replace("-", "_").replace(".", "_"): v
|
|
238
|
+
for v in list_of_potentials
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
def __getattr__(self, name):
|
|
242
|
+
if name in self._list_of_potentials:
|
|
243
|
+
return self._list_of_potentials[name]
|
|
244
|
+
else:
|
|
245
|
+
raise AttributeError
|
|
246
|
+
|
|
247
|
+
def __dir__(self):
|
|
248
|
+
return list(self._list_of_potentials.keys())
|
|
249
|
+
|
|
250
|
+
def __repr__(self):
|
|
251
|
+
return str(dir(self))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def find_potential_file_base(path, resource_path_lst, rel_path):
|
|
255
|
+
if path is not None:
|
|
256
|
+
for resource_path in resource_path_lst:
|
|
257
|
+
path_direct = os.path.join(resource_path, path)
|
|
258
|
+
path_indirect = os.path.join(resource_path, rel_path, path)
|
|
259
|
+
if os.path.exists(path_direct):
|
|
260
|
+
return path_direct
|
|
261
|
+
elif os.path.exists(path_indirect):
|
|
262
|
+
return path_indirect
|
|
263
|
+
raise ValueError(
|
|
264
|
+
"Either the filename or the functional has to be defined.",
|
|
265
|
+
path,
|
|
266
|
+
resource_path_lst,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def view_potentials(structure: Atoms, resource_path: str) -> pandas.DataFrame:
|
|
271
|
+
"""
|
|
272
|
+
List all interatomic potentials for the given atomistic structure including all potential parameters.
|
|
273
|
+
|
|
274
|
+
To quickly get only the names of the potentials you can use `list_potentials()` instead.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
structure (Atoms): The structure for which to get potentials.
|
|
278
|
+
resource_path (str): Path to the "lammps/potentials_lammps.csv" file
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
pandas.Dataframe: Dataframe including all potential parameters.
|
|
282
|
+
"""
|
|
283
|
+
list_of_elements = set(structure.get_chemical_symbols())
|
|
284
|
+
return LammpsPotentialFile(resource_path=resource_path).find(list_of_elements)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def convert_path_to_abs_posix(path: str) -> str:
|
|
288
|
+
"""
|
|
289
|
+
Convert path to an absolute POSIX path
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
path (str): input path.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
str: absolute path in POSIX format
|
|
296
|
+
"""
|
|
297
|
+
return (
|
|
298
|
+
Path(path.strip())
|
|
299
|
+
.expanduser()
|
|
300
|
+
.resolve()
|
|
301
|
+
.absolute()
|
|
302
|
+
.as_posix()
|
|
303
|
+
.replace("\\", "/")
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def update_potential_paths(
|
|
308
|
+
df_pot: pandas.DataFrame, resource_path: str
|
|
309
|
+
) -> pandas.DataFrame:
|
|
310
|
+
config_lst = []
|
|
311
|
+
for row in df_pot.itertuples():
|
|
312
|
+
potential_file_lst = row.Filename
|
|
313
|
+
potential_file_path_lst = [
|
|
314
|
+
os.path.join(resource_path, f) for f in potential_file_lst
|
|
315
|
+
]
|
|
316
|
+
potential_dict = {os.path.basename(f): f for f in potential_file_path_lst}
|
|
317
|
+
potential_commands = []
|
|
318
|
+
for line in row.Config:
|
|
319
|
+
line = line.replace("\n", "")
|
|
320
|
+
for key, value in potential_dict.items():
|
|
321
|
+
line = line.replace(key, value)
|
|
322
|
+
potential_commands.append(line)
|
|
323
|
+
config_lst.append(potential_commands)
|
|
324
|
+
df_pot["Config"] = config_lst
|
|
325
|
+
return df_pot
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def get_resource_path_from_conda(
|
|
329
|
+
env_variables: tuple[str] = ("CONDA_PREFIX", "CONDA_DIR"),
|
|
330
|
+
) -> str:
|
|
331
|
+
env = os.environ
|
|
332
|
+
for conda_var in env_variables:
|
|
333
|
+
if conda_var in env:
|
|
334
|
+
resource_path = os.path.join(env[conda_var], "share", "iprpy")
|
|
335
|
+
if os.path.exists(resource_path):
|
|
336
|
+
return resource_path
|
|
337
|
+
raise ValueError("No resource_path found" + potential_installation)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def get_potential_dataframe(structure: Atoms, resource_path=None):
|
|
341
|
+
if resource_path is None:
|
|
342
|
+
resource_path = get_resource_path_from_conda()
|
|
343
|
+
return update_potential_paths(
|
|
344
|
+
df_pot=view_potentials(structure=structure, resource_path=resource_path),
|
|
345
|
+
resource_path=resource_path,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def get_potential_by_name(potential_name: str, resource_path: Optional[str] = None):
|
|
350
|
+
if resource_path is None:
|
|
351
|
+
resource_path = get_resource_path_from_conda()
|
|
352
|
+
df = LammpsPotentialFile(resource_path=resource_path).list()
|
|
353
|
+
return update_potential_paths(
|
|
354
|
+
df_pot=df[df.Name == potential_name], resource_path=resource_path
|
|
355
|
+
).iloc[0]
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def validate_potential_dataframe(
|
|
359
|
+
potential_dataframe: pandas.DataFrame,
|
|
360
|
+
) -> pandas.DataFrame:
|
|
361
|
+
if isinstance(potential_dataframe, pandas.Series):
|
|
362
|
+
return potential_dataframe
|
|
363
|
+
elif isinstance(potential_dataframe, pandas.DataFrame):
|
|
364
|
+
if len(potential_dataframe) == 1:
|
|
365
|
+
return potential_dataframe.iloc[0]
|
|
366
|
+
elif len(potential_dataframe) == 0:
|
|
367
|
+
raise ValueError(
|
|
368
|
+
"The potential_dataframe is an empty pandas.DataFrame:",
|
|
369
|
+
potential_dataframe,
|
|
370
|
+
)
|
|
371
|
+
else:
|
|
372
|
+
raise ValueError(
|
|
373
|
+
"The potential_dataframe contains more than one interatomic potential, please select one:",
|
|
374
|
+
potential_dataframe,
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
raise TypeError(
|
|
378
|
+
"The potential_dataframe should be a pandas.DataFrame or pandas.Series, but instead it is of type:",
|
|
379
|
+
type(potential_dataframe),
|
|
380
|
+
)
|