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.
@@ -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
+ )