calphy 1.4.4__py3-none-any.whl → 1.4.6__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.
- calphy/__init__.py +1 -1
- calphy/clitools.py +1 -0
- calphy/composition_transformation.py +183 -115
- calphy/helpers.py +42 -28
- calphy/input.py +122 -17
- calphy/phase_diagram.py +195 -53
- calphy/postprocessing.py +9 -9
- calphy/scheduler.py +100 -105
- {calphy-1.4.4.dist-info → calphy-1.4.6.dist-info}/METADATA +1 -1
- calphy-1.4.6.dist-info/RECORD +25 -0
- calphy-1.4.4.dist-info/RECORD +0 -25
- {calphy-1.4.4.dist-info → calphy-1.4.6.dist-info}/WHEEL +0 -0
- {calphy-1.4.4.dist-info → calphy-1.4.6.dist-info}/entry_points.txt +0 -0
- {calphy-1.4.4.dist-info → calphy-1.4.6.dist-info}/licenses/LICENSE +0 -0
- {calphy-1.4.4.dist-info → calphy-1.4.6.dist-info}/top_level.txt +0 -0
calphy/__init__.py
CHANGED
calphy/clitools.py
CHANGED
|
@@ -3,14 +3,14 @@ calphy: a Python library and command line interface for automated free
|
|
|
3
3
|
energy calculations.
|
|
4
4
|
|
|
5
5
|
Copyright 2021 (c) Sarath Menon^1, Yury Lysogorskiy^2, Ralf Drautz^2
|
|
6
|
-
^1: Max Planck Institut für Eisenforschung, Dusseldorf, Germany
|
|
6
|
+
^1: Max Planck Institut für Eisenforschung, Dusseldorf, Germany
|
|
7
7
|
^2: Ruhr-University Bochum, Bochum, Germany
|
|
8
8
|
|
|
9
|
-
calphy is published and distributed under the Academic Software License v1.0 (ASL).
|
|
10
|
-
calphy is distributed in the hope that it will be useful for non-commercial academic research,
|
|
11
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
9
|
+
calphy is published and distributed under the Academic Software License v1.0 (ASL).
|
|
10
|
+
calphy is distributed in the hope that it will be useful for non-commercial academic research,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
12
|
calphy API is published and distributed under the BSD 3-Clause "New" or "Revised" License
|
|
13
|
-
See the LICENSE FILE for more details.
|
|
13
|
+
See the LICENSE FILE for more details.
|
|
14
14
|
|
|
15
15
|
More information about the program can be found in:
|
|
16
16
|
Menon, Sarath, Yury Lysogorskiy, Jutta Rogal, and Ralf Drautz.
|
|
@@ -32,9 +32,10 @@ from ase.atoms import Atoms
|
|
|
32
32
|
from pyscal3.core import element_dict
|
|
33
33
|
from calphy.integrators import kb
|
|
34
34
|
|
|
35
|
+
|
|
35
36
|
class CompositionTransformation:
|
|
36
37
|
"""
|
|
37
|
-
Class for performing composition transformations and
|
|
38
|
+
Class for performing composition transformations and
|
|
38
39
|
generating necessary pair styles for such transformations.
|
|
39
40
|
|
|
40
41
|
Parameters
|
|
@@ -43,11 +44,11 @@ class CompositionTransformation:
|
|
|
43
44
|
input structure which is used for composition transformation
|
|
44
45
|
|
|
45
46
|
input_chemical_formula: dict
|
|
46
|
-
dictionary of input chemical
|
|
47
|
+
dictionary of input chemical
|
|
47
48
|
|
|
48
49
|
output_chemical_formula: string
|
|
49
50
|
the required chemical composition string
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
restrictions: list of strings, optional
|
|
52
53
|
Can be used to specify restricted transformations
|
|
53
54
|
|
|
@@ -74,17 +75,17 @@ class CompositionTransformation:
|
|
|
74
75
|
transformations should not take place. The code for this is:
|
|
75
76
|
|
|
76
77
|
```
|
|
77
|
-
comp = CompositionTransformation(filename, {"Al":500, "Li":5},
|
|
78
|
+
comp = CompositionTransformation(filename, {"Al":500, "Li":5},
|
|
78
79
|
{"Al": 494, "Li": 2, "O": 3, "C":1}, restrictions=["Al-O"])
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
If the restrictions are not satisfiable, an error will be raised.
|
|
82
|
+
If the restrictions are not satisfiable, an error will be raised.
|
|
82
83
|
|
|
83
84
|
The LAMMPS data file or dump files do not contain any information about the species except the type numbers.
|
|
84
85
|
In general the number of atoms are respected, for example if the file has 10 atoms of type 1, 5 of type 2,
|
|
85
86
|
and 1 of type 3. If the `input_chemical_composition` is `{"Li": 5, "Al": 10, "O": 1}`, type 1 is assigned to Al,
|
|
86
87
|
type 2 is assigned to Li and type 3 is assigned to O. This is done irrespective of the order in which
|
|
87
|
-
`input_chemical_composition` is specified. However, if there are equal number of atoms, the order is respected.
|
|
88
|
+
`input_chemical_composition` is specified. However, if there are equal number of atoms, the order is respected.
|
|
88
89
|
Therefore it is important to make sure that the `input_chemical_composition` is in the same order as that of
|
|
89
90
|
types in structure file. For example, consider a NiAl structure of 10 Ni atoms and 10 Al atoms. Ni atoms are type 1 in LAMMPS terminology
|
|
90
91
|
and Al atoms are type 2. In this case, to preserve the order, `input_chemical_composition` should be `{"Ni": 5, "Al": 10}`.
|
|
@@ -112,22 +113,27 @@ class CompositionTransformation:
|
|
|
112
113
|
```
|
|
113
114
|
The output is written in LAMMPS dump format.
|
|
114
115
|
"""
|
|
116
|
+
|
|
115
117
|
def __init__(self, calc):
|
|
116
|
-
|
|
117
|
-
self.input_chemical_composition =
|
|
118
|
-
|
|
118
|
+
|
|
119
|
+
self.input_chemical_composition = (
|
|
120
|
+
calc.composition_scaling._input_chemical_composition
|
|
121
|
+
)
|
|
122
|
+
self.output_chemical_composition = (
|
|
123
|
+
calc.composition_scaling.output_chemical_composition
|
|
124
|
+
)
|
|
119
125
|
self.restrictions = calc.composition_scaling.restrictions
|
|
120
126
|
self.calc = calc
|
|
121
127
|
self.actual_species = None
|
|
122
128
|
self.new_species = None
|
|
123
|
-
self.maxtype = None
|
|
129
|
+
self.maxtype = None
|
|
124
130
|
self.atom_mark = None
|
|
125
131
|
self.atom_species = None
|
|
126
132
|
self.mappings = None
|
|
127
133
|
self.unique_mappings = None
|
|
128
134
|
self.mappingdict = None
|
|
129
135
|
self.prepare_mappings()
|
|
130
|
-
|
|
136
|
+
|
|
131
137
|
def dict_to_string(self, inputdict):
|
|
132
138
|
strlst = []
|
|
133
139
|
for key, val in inputdict.items():
|
|
@@ -141,49 +147,51 @@ class CompositionTransformation:
|
|
|
141
147
|
Find the entropy entribution of the transformation. To get
|
|
142
148
|
free energies, multiply by -T.
|
|
143
149
|
"""
|
|
150
|
+
|
|
144
151
|
def _log(val):
|
|
145
152
|
if val == 0:
|
|
146
153
|
return 0
|
|
147
154
|
else:
|
|
148
155
|
return np.log(val)
|
|
156
|
+
|
|
149
157
|
ents = []
|
|
150
158
|
for key, val in self.output_chemical_composition.items():
|
|
151
159
|
if key in self.input_chemical_composition.keys():
|
|
152
|
-
t1 = self.input_chemical_composition[key]/self.natoms
|
|
153
|
-
t2 = self.output_chemical_composition[key]/self.natoms
|
|
154
|
-
cont = t2*_log(t2) - t1*_log(t1)
|
|
160
|
+
t1 = self.input_chemical_composition[key] / self.natoms
|
|
161
|
+
t2 = self.output_chemical_composition[key] / self.natoms
|
|
162
|
+
cont = t2 * _log(t2) - t1 * _log(t1)
|
|
155
163
|
else:
|
|
156
164
|
t1 = 0
|
|
157
|
-
t2 = self.output_chemical_composition[key]/self.natoms
|
|
158
|
-
cont =
|
|
165
|
+
t2 = self.output_chemical_composition[key] / self.natoms
|
|
166
|
+
cont = t2 * _log(t2) - 0
|
|
159
167
|
ents.append(cont)
|
|
160
|
-
entropy_term = kb*np.sum(ents)
|
|
168
|
+
entropy_term = kb * np.sum(ents)
|
|
161
169
|
return entropy_term
|
|
162
|
-
|
|
170
|
+
|
|
163
171
|
def convert_to_pyscal(self):
|
|
164
172
|
"""
|
|
165
173
|
Convert a given system to pyscal and give a dict of type mappings
|
|
166
174
|
"""
|
|
167
|
-
aseobj = read(self.calc.lattice, format=
|
|
168
|
-
pstruct = pc.System(aseobj, format=
|
|
169
|
-
|
|
170
|
-
#here we have to validate the input composition dict; and map it
|
|
175
|
+
aseobj = read(self.calc.lattice, format="lammps-data", style="atomic")
|
|
176
|
+
pstruct = pc.System(aseobj, format="ase")
|
|
177
|
+
|
|
178
|
+
# here we have to validate the input composition dict; and map it
|
|
171
179
|
typelist = pstruct.atoms.species
|
|
172
180
|
types, typecounts = np.unique(typelist, return_counts=True)
|
|
173
181
|
composition = {types[x]: typecounts[x] for x in range(len(types))}
|
|
174
182
|
|
|
175
183
|
atomsymbols = self.calc.element
|
|
176
|
-
atomtypes = [x+1 for x in range(len(self.calc.element))]
|
|
177
|
-
|
|
184
|
+
atomtypes = [x + 1 for x in range(len(self.calc.element))]
|
|
185
|
+
|
|
178
186
|
self.pyscal_structure = pstruct
|
|
179
187
|
self.typedict = dict(zip(atomsymbols, atomtypes))
|
|
180
188
|
self.reversetypedict = dict(zip(atomtypes, atomsymbols))
|
|
181
189
|
self.natoms = self.pyscal_structure.natoms
|
|
182
|
-
|
|
190
|
+
|
|
183
191
|
self.actual_species = len(self.typedict)
|
|
184
192
|
self.new_species = len(self.output_chemical_composition) - len(self.typedict)
|
|
185
|
-
self.maxtype = self.actual_species + 1
|
|
186
|
-
#print(self.typedict)
|
|
193
|
+
self.maxtype = self.actual_species + 1 # + self.new_species
|
|
194
|
+
# print(self.typedict)
|
|
187
195
|
|
|
188
196
|
def get_composition_transformation(self):
|
|
189
197
|
"""
|
|
@@ -203,7 +211,7 @@ class CompositionTransformation:
|
|
|
203
211
|
to_remove[key] = np.abs(val)
|
|
204
212
|
else:
|
|
205
213
|
to_add[key] = val
|
|
206
|
-
|
|
214
|
+
|
|
207
215
|
self.to_remove = to_remove
|
|
208
216
|
self.to_add = to_add
|
|
209
217
|
|
|
@@ -211,33 +219,33 @@ class CompositionTransformation:
|
|
|
211
219
|
"""
|
|
212
220
|
Get a random index of a given species
|
|
213
221
|
"""
|
|
214
|
-
ids = [count for count, x in enumerate(self.atom_type) if x==species]
|
|
222
|
+
ids = [count for count, x in enumerate(self.atom_type) if x == species]
|
|
215
223
|
return ids[np.random.randint(0, len(ids))]
|
|
216
|
-
|
|
224
|
+
|
|
217
225
|
def mark_atoms(self):
|
|
218
226
|
for i in range(self.natoms):
|
|
219
227
|
self.atom_mark.append(False)
|
|
220
|
-
|
|
228
|
+
|
|
221
229
|
self.atom_type = self.pyscal_structure.atoms.types
|
|
222
230
|
self.mappings = [f"{x}-{x}" for x in self.atom_type]
|
|
223
|
-
|
|
231
|
+
|
|
224
232
|
def update_mark_atoms(self):
|
|
225
233
|
self.marked_atoms = []
|
|
226
234
|
for key, val in self.to_remove.items():
|
|
227
|
-
#print(f"Element {key}, count {val}")
|
|
235
|
+
# print(f"Element {key}, count {val}")
|
|
228
236
|
for i in range(100000):
|
|
229
237
|
rint = self.get_random_index_of_species(self.typedict[key])
|
|
230
238
|
if rint not in self.marked_atoms:
|
|
231
239
|
self.atom_mark[rint] = True
|
|
232
240
|
self.marked_atoms.append(rint)
|
|
233
241
|
val -= 1
|
|
234
|
-
if
|
|
235
|
-
break
|
|
236
|
-
|
|
242
|
+
if val <= 0:
|
|
243
|
+
break
|
|
244
|
+
|
|
237
245
|
def update_typedicts(self):
|
|
238
|
-
#in a cycle add things to the typedict
|
|
246
|
+
# in a cycle add things to the typedict
|
|
239
247
|
for key, val in self.to_add.items():
|
|
240
|
-
#print(f"Element {key}, count {val}")
|
|
248
|
+
# print(f"Element {key}, count {val}")
|
|
241
249
|
if key in self.typedict.keys():
|
|
242
250
|
newtype = self.typedict[key]
|
|
243
251
|
else:
|
|
@@ -245,61 +253,73 @@ class CompositionTransformation:
|
|
|
245
253
|
self.typedict[key] = self.maxtype
|
|
246
254
|
self.reversetypedict[self.maxtype] = key
|
|
247
255
|
self.maxtype += 1
|
|
248
|
-
#print(f"Element {key}, newtype {newtype}")
|
|
249
|
-
|
|
256
|
+
# print(f"Element {key}, newtype {newtype}")
|
|
257
|
+
|
|
250
258
|
def compute_possible_mappings(self):
|
|
251
259
|
self.possible_mappings = []
|
|
252
|
-
#Now make a list of possible mappings
|
|
260
|
+
# Now make a list of possible mappings
|
|
253
261
|
for key1, val1 in self.to_remove.items():
|
|
254
262
|
for key2, val2 in self.to_add.items():
|
|
255
263
|
mapping = f"{key1}-{key2}"
|
|
256
264
|
if mapping not in self.restrictions:
|
|
257
|
-
self.possible_mappings.append(
|
|
258
|
-
|
|
265
|
+
self.possible_mappings.append(
|
|
266
|
+
f"{self.typedict[key1]}-{self.typedict[key2]}"
|
|
267
|
+
)
|
|
268
|
+
|
|
259
269
|
def update_mappings(self):
|
|
260
270
|
marked_atoms = self.marked_atoms.copy()
|
|
261
271
|
for key, val in self.to_add.items():
|
|
262
|
-
#now get all
|
|
263
|
-
|
|
264
|
-
#we to see if we can get val number of atoms from marked ones
|
|
272
|
+
# now get all
|
|
273
|
+
|
|
274
|
+
# we to see if we can get val number of atoms from marked ones
|
|
265
275
|
if val < len(marked_atoms):
|
|
266
|
-
raise ValueError(
|
|
267
|
-
|
|
268
|
-
|
|
276
|
+
raise ValueError(
|
|
277
|
+
f"Not enough atoms to choose {val} from {len(marked_atoms)} not possible"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# otherwise find atoms until we find enough
|
|
269
281
|
for i in range(val):
|
|
270
|
-
#choose a number from marked atoms
|
|
282
|
+
# choose a number from marked atoms
|
|
271
283
|
found = False
|
|
272
284
|
to_del = []
|
|
273
285
|
for x in range(len(self.marked_atoms)):
|
|
274
286
|
random_choice = np.random.choice(marked_atoms)
|
|
275
|
-
#find corresponding mappiong
|
|
287
|
+
# find corresponding mappiong
|
|
276
288
|
mapping = f"{self.atom_type[random_choice]}-{self.typedict[key]}"
|
|
277
289
|
if mapping in self.possible_mappings:
|
|
278
|
-
#this is a valid choice
|
|
290
|
+
# this is a valid choice
|
|
279
291
|
self.mappings[random_choice] = mapping
|
|
280
292
|
found = True
|
|
281
293
|
if found:
|
|
282
|
-
#finish up, change the array, and break
|
|
283
|
-
#to_del.append(random_choice)
|
|
294
|
+
# finish up, change the array, and break
|
|
295
|
+
# to_del.append(random_choice)
|
|
284
296
|
marked_atoms.remove(random_choice)
|
|
285
297
|
break
|
|
286
|
-
#if it was not found, the loop finished, throw error
|
|
298
|
+
# if it was not found, the loop finished, throw error
|
|
287
299
|
if not found:
|
|
288
|
-
raise ValueError(
|
|
289
|
-
|
|
290
|
-
|
|
300
|
+
raise ValueError(
|
|
301
|
+
"A possible transformation could not be found, please check the restrictions"
|
|
302
|
+
)
|
|
303
|
+
# otherwise modify our marked atoms, list, and move on
|
|
304
|
+
# for item in to_del:
|
|
291
305
|
# marked_atoms.remove(item)
|
|
292
|
-
|
|
293
|
-
self.unique_mappings, self.unique_mapping_counts = np.unique(self.mappings, return_counts=True)
|
|
294
306
|
|
|
295
|
-
|
|
307
|
+
self.unique_mappings, self.unique_mapping_counts = np.unique(
|
|
308
|
+
self.mappings, return_counts=True
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# now make the transformation dict
|
|
296
312
|
self.transformation_list = []
|
|
297
313
|
for count, mapping in enumerate(self.unique_mappings):
|
|
298
314
|
mapsplit = mapping.split("-")
|
|
299
315
|
if not mapsplit[0] == mapsplit[1]:
|
|
300
316
|
transformation_dict = {}
|
|
301
|
-
transformation_dict["primary_element"] = self.reversetypedict[
|
|
302
|
-
|
|
317
|
+
transformation_dict["primary_element"] = self.reversetypedict[
|
|
318
|
+
int(mapsplit[0])
|
|
319
|
+
]
|
|
320
|
+
transformation_dict["secondary_element"] = self.reversetypedict[
|
|
321
|
+
int(mapsplit[1])
|
|
322
|
+
]
|
|
303
323
|
transformation_dict["count"] = self.unique_mapping_counts[count]
|
|
304
324
|
self.transformation_list.append(transformation_dict)
|
|
305
325
|
|
|
@@ -307,59 +327,107 @@ class CompositionTransformation:
|
|
|
307
327
|
self.update_typedicts()
|
|
308
328
|
self.compute_possible_mappings()
|
|
309
329
|
self.update_mappings()
|
|
310
|
-
|
|
330
|
+
|
|
311
331
|
def prepare_pair_lists(self):
|
|
312
332
|
self.pair_list_old = []
|
|
313
333
|
self.pair_list_new = []
|
|
314
334
|
for mapping in self.unique_mappings:
|
|
315
335
|
map_split = mapping.split("-")
|
|
316
|
-
#conserved atom
|
|
317
|
-
if
|
|
336
|
+
# conserved atom
|
|
337
|
+
if map_split[0] == map_split[1]:
|
|
318
338
|
self.pair_list_old.append(self.reversetypedict[int(map_split[0])])
|
|
319
339
|
self.pair_list_new.append(self.reversetypedict[int(map_split[0])])
|
|
320
340
|
else:
|
|
321
341
|
self.pair_list_old.append(self.reversetypedict[int(map_split[0])])
|
|
322
|
-
self.pair_list_new.append(self.reversetypedict[int(map_split[1])])
|
|
323
|
-
self.new_atomtype = np.array(range(len(self.unique_mappings)))+1
|
|
342
|
+
self.pair_list_new.append(self.reversetypedict[int(map_split[1])])
|
|
343
|
+
self.new_atomtype = np.array(range(len(self.unique_mappings))) + 1
|
|
324
344
|
self.mappingdict = dict(zip(self.unique_mappings, self.new_atomtype))
|
|
325
|
-
|
|
345
|
+
|
|
326
346
|
def update_types(self):
|
|
327
347
|
for x in range(len(self.atom_type)):
|
|
328
348
|
self.atom_type[x] = self.mappingdict[self.mappings[x]]
|
|
329
|
-
|
|
330
|
-
#smartify these loops
|
|
331
|
-
#npyscal = len(self.pyscal_structure.atoms.types)
|
|
349
|
+
|
|
350
|
+
# smartify these loops
|
|
351
|
+
# npyscal = len(self.pyscal_structure.atoms.types)
|
|
332
352
|
self.pyscal_structure.atoms.types = self.atom_type
|
|
333
|
-
#for count in range(npyscal)):
|
|
353
|
+
# for count in range(npyscal)):
|
|
334
354
|
# self.pyscal_structure.atoms.types[count] = self.atom_type[count]
|
|
335
|
-
|
|
355
|
+
|
|
336
356
|
def iselement(self, symbol):
|
|
337
357
|
try:
|
|
338
358
|
_ = element(symbol)
|
|
339
359
|
return True
|
|
340
360
|
except:
|
|
341
361
|
return False
|
|
342
|
-
|
|
362
|
+
|
|
343
363
|
def update_pair_coeff(self, pair_coeff):
|
|
364
|
+
"""
|
|
365
|
+
Update pair_coeff command with new element specifications.
|
|
366
|
+
|
|
367
|
+
Handles both single-file formats (EAM alloy):
|
|
368
|
+
pair_coeff * * potential.eam.alloy El1 El2
|
|
369
|
+
|
|
370
|
+
And two-file formats (MEAM):
|
|
371
|
+
pair_coeff * * library.meam El1 El2 potential.meam El1 El2
|
|
372
|
+
|
|
373
|
+
For MEAM potentials, both element specifications are updated identically.
|
|
374
|
+
"""
|
|
344
375
|
pcsplit = pair_coeff.strip().split()
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
376
|
+
result_parts = []
|
|
377
|
+
i = 0
|
|
378
|
+
|
|
379
|
+
while i < len(pcsplit):
|
|
380
|
+
token = pcsplit[i]
|
|
381
|
+
|
|
382
|
+
# Check if this token starts an element specification
|
|
383
|
+
# (either it's an element, or the next token is an element)
|
|
384
|
+
if self.iselement(token):
|
|
385
|
+
# Found start of element list - collect all consecutive elements
|
|
386
|
+
element_group = []
|
|
387
|
+
while i < len(pcsplit) and self.iselement(pcsplit[i]):
|
|
388
|
+
element_group.append(pcsplit[i])
|
|
389
|
+
i += 1
|
|
390
|
+
|
|
391
|
+
# Determine which element list to use based on what we found
|
|
392
|
+
# If element_group matches our pair_list_old, replace with pair_list_new
|
|
393
|
+
# Otherwise replace with pair_list_old (for the old/reference command)
|
|
394
|
+
if element_group == self.pair_list_old or set(element_group) == set(
|
|
395
|
+
self.element
|
|
396
|
+
):
|
|
397
|
+
# This needs special handling - we'll mark position for later
|
|
398
|
+
result_parts.append("__ELEMENTS__")
|
|
399
|
+
else:
|
|
400
|
+
# Keep non-matching element groups as-is
|
|
401
|
+
result_parts.extend(element_group)
|
|
402
|
+
else:
|
|
403
|
+
# Non-element token (potential file, wildcards, options, etc.)
|
|
404
|
+
result_parts.append(token)
|
|
405
|
+
i += 1
|
|
406
|
+
|
|
407
|
+
# Now build old and new commands by replacing __ELEMENTS__ markers
|
|
408
|
+
pc_old_parts = [
|
|
409
|
+
self.pair_list_old if p == "__ELEMENTS__" else [p] for p in result_parts
|
|
410
|
+
]
|
|
411
|
+
pc_new_parts = [
|
|
412
|
+
self.pair_list_new if p == "__ELEMENTS__" else [p] for p in result_parts
|
|
413
|
+
]
|
|
414
|
+
|
|
415
|
+
# Flatten the lists
|
|
416
|
+
pc_old = " ".join(
|
|
417
|
+
[
|
|
418
|
+
item
|
|
419
|
+
for sublist in pc_old_parts
|
|
420
|
+
for item in (sublist if isinstance(sublist, list) else [sublist])
|
|
421
|
+
]
|
|
422
|
+
)
|
|
423
|
+
pc_new = " ".join(
|
|
424
|
+
[
|
|
425
|
+
item
|
|
426
|
+
for sublist in pc_new_parts
|
|
427
|
+
for item in (sublist if isinstance(sublist, list) else [sublist])
|
|
428
|
+
]
|
|
429
|
+
)
|
|
430
|
+
|
|
363
431
|
return pc_old, pc_new
|
|
364
432
|
|
|
365
433
|
def get_swap_types(self):
|
|
@@ -369,42 +437,42 @@ class CompositionTransformation:
|
|
|
369
437
|
swap_list = []
|
|
370
438
|
for mapping in self.unique_mappings:
|
|
371
439
|
map_split = mapping.split("-")
|
|
372
|
-
#conserved atom
|
|
373
|
-
if
|
|
440
|
+
# conserved atom
|
|
441
|
+
if map_split[0] == map_split[1]:
|
|
374
442
|
pass
|
|
375
443
|
else:
|
|
376
444
|
first_type = map_split[0]
|
|
377
445
|
second_type = map_split[1]
|
|
378
|
-
first_map = f
|
|
446
|
+
first_map = f"{first_type}-{first_type}"
|
|
379
447
|
second_map = mapping
|
|
380
448
|
|
|
381
|
-
#get the numbers from dict
|
|
449
|
+
# get the numbers from dict
|
|
382
450
|
first_swap_type = self.mappingdict[first_map]
|
|
383
451
|
second_swap_type = self.mappingdict[second_map]
|
|
384
452
|
|
|
385
453
|
swap_list.append([first_swap_type, second_swap_type])
|
|
386
454
|
return swap_list[0]
|
|
387
|
-
|
|
388
|
-
|
|
455
|
+
|
|
389
456
|
def write_structure(self, outfilename):
|
|
390
|
-
#create some species dict
|
|
391
|
-
#just to trick ase to write
|
|
457
|
+
# create some species dict
|
|
458
|
+
# just to trick ase to write
|
|
392
459
|
utypes = np.unique(self.pyscal_structure.atoms["types"])
|
|
393
|
-
element_list = list(element_dict.keys())
|
|
394
|
-
element_type_dict = {
|
|
395
|
-
|
|
460
|
+
element_list = list(element_dict.keys())
|
|
461
|
+
element_type_dict = {
|
|
462
|
+
str(u): element_list[count] for count, u in enumerate(utypes)
|
|
463
|
+
}
|
|
464
|
+
species = [
|
|
465
|
+
element_type_dict[str(x)] for x in self.pyscal_structure.atoms["types"]
|
|
466
|
+
]
|
|
396
467
|
self.pyscal_structure.atoms["species"] = species
|
|
397
|
-
self.pyscal_structure.write.file(outfilename, format=
|
|
398
|
-
|
|
468
|
+
self.pyscal_structure.write.file(outfilename, format="lammps-data")
|
|
399
469
|
|
|
400
|
-
|
|
401
|
-
|
|
402
470
|
def prepare_mappings(self):
|
|
403
471
|
self.atom_mark = []
|
|
404
472
|
self.atom_species = []
|
|
405
473
|
self.mappings = []
|
|
406
474
|
self.unique_mappings = []
|
|
407
|
-
|
|
475
|
+
|
|
408
476
|
self.get_composition_transformation()
|
|
409
477
|
self.convert_to_pyscal()
|
|
410
478
|
|
|
@@ -412,4 +480,4 @@ class CompositionTransformation:
|
|
|
412
480
|
self.update_mark_atoms()
|
|
413
481
|
self.get_mappings()
|
|
414
482
|
self.prepare_pair_lists()
|
|
415
|
-
self.update_types()
|
|
483
|
+
self.update_types()
|