calphy 1.2.14__tar.gz → 1.3.2__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.
- {calphy-1.2.14/calphy.egg-info → calphy-1.3.2}/PKG-INFO +1 -2
- {calphy-1.2.14 → calphy-1.3.2}/calphy/__init__.py +1 -1
- {calphy-1.2.14 → calphy-1.3.2}/calphy/alchemy.py +9 -4
- {calphy-1.2.14 → calphy-1.3.2}/calphy/helpers.py +20 -190
- {calphy-1.2.14 → calphy-1.3.2}/calphy/input.py +72 -62
- {calphy-1.2.14 → calphy-1.3.2}/calphy/integrators.py +61 -34
- {calphy-1.2.14 → calphy-1.3.2}/calphy/liquid.py +11 -5
- {calphy-1.2.14 → calphy-1.3.2}/calphy/phase.py +31 -76
- {calphy-1.2.14 → calphy-1.3.2}/calphy/routines.py +5 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/scheduler.py +2 -3
- {calphy-1.2.14 → calphy-1.3.2}/calphy/solid.py +37 -18
- {calphy-1.2.14 → calphy-1.3.2/calphy.egg-info}/PKG-INFO +1 -2
- {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/SOURCES.txt +0 -4
- {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/requires.txt +0 -1
- {calphy-1.2.14 → calphy-1.3.2}/setup.py +2 -2
- {calphy-1.2.14 → calphy-1.3.2}/tests/test_integrators.py +0 -4
- {calphy-1.2.14 → calphy-1.3.2}/tests/test_solid_methods.py +0 -1
- calphy-1.2.14/calphy/inputbk.py +0 -862
- calphy-1.2.14/calphy/lattice.py +0 -180
- calphy-1.2.14/tests/test_liquid_avg.py +0 -33
- calphy-1.2.14/tests/test_solid_avg.py +0 -34
- {calphy-1.2.14 → calphy-1.3.2}/LICENSE +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/MANIFEST.in +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/README.md +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/clitools.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/composition_transformation.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/errors.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/kernel.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/phase_diagram.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/queuekernel.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy/splines.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/dependency_links.txt +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/entry_points.txt +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/not-zip-safe +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/top_level.txt +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/setup.cfg +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/tests/test_helpers.py +0 -0
- {calphy-1.2.14 → calphy-1.3.2}/tests/test_options.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: calphy
|
|
3
|
-
Version: 1.2
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: free energy calculation for python
|
|
5
5
|
Home-page: https://github.com/ICAMS/calphy
|
|
6
6
|
Author: Sarath Menon, Yury Lysogorskiy, Ralf Drautz
|
|
@@ -21,7 +21,6 @@ Requires-Dist: mendeleev
|
|
|
21
21
|
Requires-Dist: tqdm
|
|
22
22
|
Requires-Dist: scipy
|
|
23
23
|
Requires-Dist: pydantic
|
|
24
|
-
Requires-Dist: pyscal
|
|
25
24
|
Requires-Dist: pyscal3
|
|
26
25
|
|
|
27
26
|
# calphy
|
|
@@ -24,9 +24,7 @@ sarath.menon@ruhr-uni-bochum.de/yury.lysogorskiy@icams.rub.de
|
|
|
24
24
|
import numpy as np
|
|
25
25
|
import yaml
|
|
26
26
|
|
|
27
|
-
import pyscal.traj_process as ptp
|
|
28
27
|
from calphy.integrators import *
|
|
29
|
-
import calphy.lattice as pl
|
|
30
28
|
import calphy.helpers as ph
|
|
31
29
|
import calphy.phase as cph
|
|
32
30
|
|
|
@@ -79,11 +77,14 @@ class Alchemy(cph.Phase):
|
|
|
79
77
|
lmp = ph.create_object(self.cores, self.simfolder, self.calc.md.timestep,
|
|
80
78
|
self.calc.md.cmdargs, self.calc.md.init_commands)
|
|
81
79
|
|
|
80
|
+
lmp.command(f'pair_style {self.calc._pair_style_with_options[0]}')
|
|
81
|
+
|
|
82
82
|
#set up structure
|
|
83
83
|
lmp = ph.create_structure(lmp, self.calc)
|
|
84
84
|
|
|
85
85
|
#set up potential
|
|
86
|
-
lmp
|
|
86
|
+
lmp.command(f'pair_coeff {self.calc.pair_coeff[0]}')
|
|
87
|
+
lmp = ph.set_mass(lmp, self.calc)
|
|
87
88
|
|
|
88
89
|
#add some computes
|
|
89
90
|
lmp.command("variable mvol equal vol")
|
|
@@ -145,6 +146,8 @@ class Alchemy(cph.Phase):
|
|
|
145
146
|
# Adiabatic switching parameters.
|
|
146
147
|
lmp.command("variable li equal 1.0")
|
|
147
148
|
lmp.command("variable lf equal 0.0")
|
|
149
|
+
|
|
150
|
+
lmp.command(f'pair_style {self.calc._pair_style_with_options[0]}')
|
|
148
151
|
|
|
149
152
|
#read dump file
|
|
150
153
|
#conf = os.path.join(self.simfolder, "conf.equilibration.dump")
|
|
@@ -153,7 +156,9 @@ class Alchemy(cph.Phase):
|
|
|
153
156
|
|
|
154
157
|
#set up hybrid potential
|
|
155
158
|
#here we only need to set one potential
|
|
156
|
-
lmp
|
|
159
|
+
lmp.command(f'pair_coeff {self.calc.pair_coeff[0]}')
|
|
160
|
+
lmp = ph.set_mass(lmp, self.calc)
|
|
161
|
+
|
|
157
162
|
#lmp = ph.set_double_hybrid_potential(lmp, self.options, self.calc._pressureair_style, self.calc._pressureair_coeff)
|
|
158
163
|
|
|
159
164
|
#remap the box to get the correct pressure
|
|
@@ -22,16 +22,17 @@ sarath.menon@ruhr-uni-bochum.de/yury.lysogorskiy@icams.rub.de
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
import os
|
|
25
|
-
|
|
25
|
+
import shutil
|
|
26
|
+
import warnings
|
|
26
27
|
import logging
|
|
27
28
|
import numpy as np
|
|
29
|
+
|
|
30
|
+
from pylammpsmpi import LammpsLibrary
|
|
28
31
|
from lammps import lammps
|
|
29
|
-
import calphy.lattice as pl
|
|
30
|
-
import shutil
|
|
31
|
-
import pyscal.core as pc
|
|
32
32
|
from ase.io import read, write
|
|
33
|
-
|
|
34
|
-
import
|
|
33
|
+
|
|
34
|
+
import pyscal3.core as pc
|
|
35
|
+
from pyscal3.trajectory import Trajectory
|
|
35
36
|
|
|
36
37
|
class LammpsScript:
|
|
37
38
|
def __init__(self):
|
|
@@ -45,8 +46,8 @@ class LammpsScript:
|
|
|
45
46
|
for line in self.script:
|
|
46
47
|
fout.write(f'{line}\n')
|
|
47
48
|
|
|
48
|
-
def create_object(cores, directory, timestep, cmdargs=
|
|
49
|
-
init_commands=
|
|
49
|
+
def create_object(cores, directory, timestep, cmdargs="",
|
|
50
|
+
init_commands=(), script_mode=False):
|
|
50
51
|
"""
|
|
51
52
|
Create LAMMPS object
|
|
52
53
|
|
|
@@ -68,6 +69,8 @@ def create_object(cores, directory, timestep, cmdargs=None,
|
|
|
68
69
|
if script_mode:
|
|
69
70
|
lmp = LammpsScript()
|
|
70
71
|
else:
|
|
72
|
+
if cmdargs == "":
|
|
73
|
+
cmdargs = None
|
|
71
74
|
lmp = LammpsLibrary(
|
|
72
75
|
cores=cores, working_directory=directory, cmdargs=cmdargs
|
|
73
76
|
)
|
|
@@ -78,7 +81,7 @@ def create_object(cores, directory, timestep, cmdargs=None,
|
|
|
78
81
|
["timestep", str(timestep)],
|
|
79
82
|
["box", "tilt large"]]
|
|
80
83
|
|
|
81
|
-
if init_commands
|
|
84
|
+
if len(init_commands) > 0:
|
|
82
85
|
#we need to replace some initial commands
|
|
83
86
|
for rc in init_commands:
|
|
84
87
|
#split the command
|
|
@@ -117,19 +120,13 @@ def create_structure(lmp, calc):
|
|
|
117
120
|
return lmp
|
|
118
121
|
|
|
119
122
|
|
|
120
|
-
def set_mass(lmp, options
|
|
121
|
-
count = 1
|
|
123
|
+
def set_mass(lmp, options):
|
|
122
124
|
for i in range(options.n_elements):
|
|
123
125
|
lmp.command(f'mass {i+1} {options.mass[i]}')
|
|
124
|
-
count += 1
|
|
125
|
-
|
|
126
|
-
for i in range(ghost_elements):
|
|
127
|
-
lmp.command(f'mass {count+i} 1.00')
|
|
128
|
-
|
|
129
126
|
return lmp
|
|
130
127
|
|
|
131
128
|
|
|
132
|
-
def set_potential(lmp, options
|
|
129
|
+
def set_potential(lmp, options):
|
|
133
130
|
"""
|
|
134
131
|
Set the interatomic potential
|
|
135
132
|
|
|
@@ -148,50 +145,14 @@ def set_potential(lmp, options, ghost_elements=0):
|
|
|
148
145
|
lmp.command(f'pair_style {options._pair_style_with_options[0]}')
|
|
149
146
|
lmp.command(f'pair_coeff {options.pair_coeff[0]}')
|
|
150
147
|
|
|
151
|
-
lmp = set_mass(lmp, options
|
|
152
|
-
|
|
153
|
-
return lmp
|
|
148
|
+
lmp = set_mass(lmp, options)
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
def read_dump(lmp, file, species=1):
|
|
157
|
-
# Read atoms positions, velocities and box parameters.
|
|
158
|
-
lmp.command("lattice fcc 4.0")
|
|
159
|
-
lmp.command("region box block 0 2 0 2 0 2")
|
|
160
|
-
lmp.command("create_box %d box" % species)
|
|
161
|
-
lmp.command(
|
|
162
|
-
"read_dump %s 0 x y z vx vy vz scaled no box yes add keep" % file
|
|
163
|
-
)
|
|
164
|
-
lmp.command("change_box all triclinic")
|
|
165
150
|
return lmp
|
|
166
151
|
|
|
167
|
-
|
|
168
152
|
def read_data(lmp, file):
|
|
169
153
|
lmp.command(f"read_data {file}")
|
|
170
154
|
return lmp
|
|
171
155
|
|
|
172
|
-
|
|
173
|
-
def convert_to_data_file(inputfile, outputfile, ghost_elements=0):
|
|
174
|
-
atoms = read(inputfile, format="lammps-dump-text")
|
|
175
|
-
write(outputfile, atoms, format="lammps-data")
|
|
176
|
-
|
|
177
|
-
if ghost_elements > 0:
|
|
178
|
-
lines = []
|
|
179
|
-
with open(outputfile, "r") as fin:
|
|
180
|
-
for line in fin:
|
|
181
|
-
raw = line.strip().split()
|
|
182
|
-
if (len(raw) == 3) and (raw[2] == "types"):
|
|
183
|
-
raw[0] = "%d" % ghost_elements
|
|
184
|
-
raw.append("\n")
|
|
185
|
-
rline = " ".join(raw)
|
|
186
|
-
lines.append(rline)
|
|
187
|
-
else:
|
|
188
|
-
lines.append(line)
|
|
189
|
-
|
|
190
|
-
with open(outputfile, "w") as fout:
|
|
191
|
-
for line in lines:
|
|
192
|
-
fout.write(line)
|
|
193
|
-
|
|
194
|
-
|
|
195
156
|
def get_structures(file, species, index=None):
|
|
196
157
|
traj = Trajectory(file)
|
|
197
158
|
if index is None:
|
|
@@ -200,94 +161,6 @@ def get_structures(file, species, index=None):
|
|
|
200
161
|
aseobjs = traj[index].to_ase(species=species)
|
|
201
162
|
return aseobjs
|
|
202
163
|
|
|
203
|
-
|
|
204
|
-
def set_hybrid_potential(lmp, options, eps, ghost_elements=0):
|
|
205
|
-
pc = options.pair_coeff[0]
|
|
206
|
-
pcraw = pc.split()
|
|
207
|
-
pcnew = " ".join(
|
|
208
|
-
[
|
|
209
|
-
*pcraw[:2],
|
|
210
|
-
*[
|
|
211
|
-
options._pair_style_names[0],
|
|
212
|
-
],
|
|
213
|
-
*pcraw[2:],
|
|
214
|
-
]
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
lmp.command(
|
|
218
|
-
"pair_style hybrid/overlay %s ufm 7.5"
|
|
219
|
-
% options._pair_style_with_options[0]
|
|
220
|
-
)
|
|
221
|
-
lmp.command("pair_coeff %s" % pcnew)
|
|
222
|
-
lmp.command("pair_coeff * * ufm %f 1.5" % eps)
|
|
223
|
-
|
|
224
|
-
lmp = set_mass(lmp, options, ghost_elements=ghost_elements)
|
|
225
|
-
|
|
226
|
-
return lmp
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def set_double_hybrid_potential(lmp, options, ghost_elements=0):
|
|
230
|
-
|
|
231
|
-
pc1 = options.pair_coeff[0]
|
|
232
|
-
pcraw1 = pc1.split()
|
|
233
|
-
|
|
234
|
-
pc2 = options.pair_coeff[1]
|
|
235
|
-
pcraw2 = pc2.split()
|
|
236
|
-
|
|
237
|
-
if options.pair_style[0] == options.pair_style[1]:
|
|
238
|
-
pcnew1 = " ".join(
|
|
239
|
-
[
|
|
240
|
-
*pcraw1[:2],
|
|
241
|
-
*[
|
|
242
|
-
options._pair_style_names[0],
|
|
243
|
-
],
|
|
244
|
-
"1",
|
|
245
|
-
*pcraw1[2:],
|
|
246
|
-
]
|
|
247
|
-
)
|
|
248
|
-
pcnew2 = " ".join(
|
|
249
|
-
[
|
|
250
|
-
*pcraw2[:2],
|
|
251
|
-
*[
|
|
252
|
-
options._pair_style_names[1],
|
|
253
|
-
],
|
|
254
|
-
"2",
|
|
255
|
-
*pcraw2[2:],
|
|
256
|
-
]
|
|
257
|
-
)
|
|
258
|
-
else:
|
|
259
|
-
pcnew1 = " ".join(
|
|
260
|
-
[
|
|
261
|
-
*pcraw1[:2],
|
|
262
|
-
*[
|
|
263
|
-
options._pair_style_names[0],
|
|
264
|
-
],
|
|
265
|
-
*pcraw1[2:],
|
|
266
|
-
]
|
|
267
|
-
)
|
|
268
|
-
pcnew2 = " ".join(
|
|
269
|
-
[
|
|
270
|
-
*pcraw2[:2],
|
|
271
|
-
*[
|
|
272
|
-
options._pair_style_names[1],
|
|
273
|
-
],
|
|
274
|
-
*pcraw2[2:],
|
|
275
|
-
]
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
lmp.command(
|
|
279
|
-
"pair_style hybrid/overlay %s %s"
|
|
280
|
-
% (options._pair_style_with_options[0], options._pair_style_with_options[1])
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
lmp.command("pair_coeff %s" % pcnew1)
|
|
284
|
-
lmp.command("pair_coeff %s" % pcnew2)
|
|
285
|
-
|
|
286
|
-
lmp = set_mass(lmp, options, ghost_elements=ghost_elements)
|
|
287
|
-
|
|
288
|
-
return lmp
|
|
289
|
-
|
|
290
|
-
|
|
291
164
|
def remap_box(lmp, x, y, z):
|
|
292
165
|
lmp.command("run 0")
|
|
293
166
|
lmp.command(
|
|
@@ -329,15 +202,15 @@ PYSCAL helper routines
|
|
|
329
202
|
|
|
330
203
|
|
|
331
204
|
def find_solid_fraction(file):
|
|
332
|
-
sys = pc.System()
|
|
333
|
-
sys.read_inputfile(file)
|
|
205
|
+
sys = pc.System(file)
|
|
334
206
|
try:
|
|
335
|
-
sys.
|
|
207
|
+
sys.find.neighbors(method="cutoff", cutoff=0)
|
|
336
208
|
except RuntimeError:
|
|
337
|
-
sys.
|
|
209
|
+
sys.find.neighbors(
|
|
338
210
|
method="cutoff", cutoff=5.0
|
|
339
211
|
) # Maybe add value as convergence param?
|
|
340
|
-
|
|
212
|
+
sys.find.solids(cluster=False)
|
|
213
|
+
solids = np.sum(sys.atoms.solid)
|
|
341
214
|
return solids
|
|
342
215
|
|
|
343
216
|
|
|
@@ -345,49 +218,6 @@ def write_data(lmp, file):
|
|
|
345
218
|
lmp.command(f"write_data {file}")
|
|
346
219
|
return lmp
|
|
347
220
|
|
|
348
|
-
|
|
349
|
-
def reset_timestep(conf, file="current.data", init_commands=None):
|
|
350
|
-
lmp = create_object(
|
|
351
|
-
cores=1,
|
|
352
|
-
directory=os.path.dirname(file),
|
|
353
|
-
timestep=0,
|
|
354
|
-
cmdargs=None,
|
|
355
|
-
init_commands=init_commands,
|
|
356
|
-
)
|
|
357
|
-
lmp = read_data(lmp, file)
|
|
358
|
-
lmp = write_data(lmp, conf)
|
|
359
|
-
return lmp
|
|
360
|
-
|
|
361
|
-
# with open(file, "r") as f:
|
|
362
|
-
# with open(conf, "w") as c:
|
|
363
|
-
# zero = False
|
|
364
|
-
# for l in f:
|
|
365
|
-
# if zero:
|
|
366
|
-
# c.write("0\n")
|
|
367
|
-
# zero = False
|
|
368
|
-
# continue
|
|
369
|
-
# elif l.startswith("ITEM: TIMESTEP"):
|
|
370
|
-
# zero = True
|
|
371
|
-
# c.write(l)
|
|
372
|
-
|
|
373
|
-
# lmp = create_object(
|
|
374
|
-
# cores=1,
|
|
375
|
-
# directory = os.path.dirname(file),
|
|
376
|
-
# timestep=0,
|
|
377
|
-
# cmdargs=None,
|
|
378
|
-
# )
|
|
379
|
-
# lmp.command("dump 2 all custom 1 %s id type mass x y z vx vy vz"%(file))+
|
|
380
|
-
# lmp.command("reset_timestep 0")
|
|
381
|
-
# lmp.command("run 0")
|
|
382
|
-
# lmp.command("undump 2")
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
"""
|
|
386
|
-
NOrmal helper routines
|
|
387
|
-
---------------------------------------------------------------------
|
|
388
|
-
"""
|
|
389
|
-
|
|
390
|
-
|
|
391
221
|
def prepare_log(file, screen=False):
|
|
392
222
|
logger = logging.getLogger(__name__)
|
|
393
223
|
handler = logging.FileHandler(file)
|
|
@@ -40,6 +40,7 @@ from pyscal3.core import structure_dict, element_dict, _make_crystal
|
|
|
40
40
|
from ase.io import read, write
|
|
41
41
|
import shutil
|
|
42
42
|
|
|
43
|
+
__version__ = "1.3.2"
|
|
43
44
|
|
|
44
45
|
def read_report(folder):
|
|
45
46
|
"""
|
|
@@ -58,13 +59,30 @@ def _check_equal(val):
|
|
|
58
59
|
return False
|
|
59
60
|
return True
|
|
60
61
|
|
|
61
|
-
|
|
62
62
|
def to_list(v: Any) -> List[Any]:
|
|
63
63
|
return np.atleast_1d(v)
|
|
64
64
|
|
|
65
|
+
def _to_str(val):
|
|
66
|
+
if np.isscalar(val):
|
|
67
|
+
return str(val)
|
|
68
|
+
else:
|
|
69
|
+
return [str(x) for x in val]
|
|
70
|
+
|
|
71
|
+
def _to_int(val):
|
|
72
|
+
if np.isscalar(val):
|
|
73
|
+
return int(val)
|
|
74
|
+
else:
|
|
75
|
+
return [int(x) for x in val]
|
|
76
|
+
|
|
77
|
+
def _to_float(val):
|
|
78
|
+
if np.isscalar(val):
|
|
79
|
+
return float(val)
|
|
80
|
+
else:
|
|
81
|
+
return [float(x) for x in val]
|
|
82
|
+
|
|
65
83
|
class CompositionScaling(BaseModel, title='Composition scaling input options'):
|
|
66
84
|
_input_chemical_composition: PrivateAttr(default=None)
|
|
67
|
-
output_chemical_composition: Annotated[dict, Field(default=
|
|
85
|
+
output_chemical_composition: Annotated[dict, Field(default={}, required=False)]
|
|
68
86
|
restrictions: Annotated[List[str], BeforeValidator(to_list),
|
|
69
87
|
Field(default=[], required=False)]
|
|
70
88
|
|
|
@@ -78,8 +96,8 @@ class MD(BaseModel, title='MD specific input options'):
|
|
|
78
96
|
n_cycles: Annotated[int, Field(default=100, gt=0)]
|
|
79
97
|
thermostat_damping: Annotated[Union[float, conlist(float, min_length=2, max_length=2)], Field(default=0.1, gt=0)]
|
|
80
98
|
barostat_damping: Annotated[Union[float, conlist(float, min_length=2, max_length=2)], Field(default=0.1, gt=0)]
|
|
81
|
-
cmdargs: Annotated[str, Field(default=
|
|
82
|
-
init_commands: Annotated[
|
|
99
|
+
cmdargs: Annotated[str, Field(default="")]
|
|
100
|
+
init_commands: Annotated[List, Field(default=[])]
|
|
83
101
|
|
|
84
102
|
|
|
85
103
|
class NoseHoover(BaseModel, title='Specific input options for Nose-Hoover thermostat'):
|
|
@@ -94,12 +112,12 @@ class Queue(BaseModel, title='Options for configuring queue'):
|
|
|
94
112
|
scheduler: Annotated[str, Field(default='local')]
|
|
95
113
|
cores: Annotated[int, Field(default=1, gt=0)]
|
|
96
114
|
jobname: Annotated[str, Field(default='calphy')]
|
|
97
|
-
walltime: Annotated[str, Field(default=
|
|
98
|
-
queuename: Annotated[str, Field(default=
|
|
115
|
+
walltime: Annotated[str, Field(default="23:59:00")]
|
|
116
|
+
queuename: Annotated[str, Field(default="")]
|
|
99
117
|
memory: Annotated[str, Field(default="3GB")]
|
|
100
|
-
commands: Annotated[List
|
|
101
|
-
options: Annotated[List
|
|
102
|
-
modules: Annotated[List
|
|
118
|
+
commands: Annotated[List, Field(default=[])]
|
|
119
|
+
options: Annotated[List, Field(default=[])]
|
|
120
|
+
modules: Annotated[List, Field(default=[])]
|
|
103
121
|
|
|
104
122
|
class Tolerance(BaseModel, title='Tolerance settings for convergence'):
|
|
105
123
|
lattice_constant: Annotated[float, Field(default=0.0002, ge=0)]
|
|
@@ -109,7 +127,7 @@ class Tolerance(BaseModel, title='Tolerance settings for convergence'):
|
|
|
109
127
|
pressure: Annotated[float, Field(default=0.5, ge=0)]
|
|
110
128
|
|
|
111
129
|
class MeltingTemperature(BaseModel, title='Input options for melting temperature mode'):
|
|
112
|
-
guess: Annotated[float, Field(default=None, gt=0)]
|
|
130
|
+
guess: Annotated[Union[float, None], Field(default=None, gt=0)]
|
|
113
131
|
step: Annotated[int, Field(default=200, ge=20)]
|
|
114
132
|
attempts: Annotated[int, Field(default=5, ge=1)]
|
|
115
133
|
|
|
@@ -130,7 +148,7 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
130
148
|
kernel: Annotated[int, Field(default=0)]
|
|
131
149
|
inputfile: Annotated[str, Field(default='')]
|
|
132
150
|
|
|
133
|
-
mode: Annotated[str, Field(default=None)]
|
|
151
|
+
mode: Annotated[Union[str, None], Field(default=None)]
|
|
134
152
|
lattice: Annotated[str, Field(default="")]
|
|
135
153
|
file_format: Annotated[str, Field(default='lammps-data')]
|
|
136
154
|
|
|
@@ -155,13 +173,13 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
155
173
|
|
|
156
174
|
melting_cycle: Annotated[ bool, Field(default=True)]
|
|
157
175
|
|
|
158
|
-
pair_style: Annotated[ List[str], BeforeValidator(to_list),
|
|
176
|
+
pair_style: Annotated[ Union[List[str], None], BeforeValidator(to_list),
|
|
159
177
|
Field(default=None)]
|
|
160
|
-
pair_coeff: Annotated[ List[str], BeforeValidator(to_list),
|
|
178
|
+
pair_coeff: Annotated[ Union[List[str], None], BeforeValidator(to_list),
|
|
161
179
|
Field(default=None)]
|
|
162
|
-
potential_file: Annotated[ str, Field(default=None)]
|
|
180
|
+
potential_file: Annotated[ Union[str,None], Field(default=None)]
|
|
163
181
|
fix_potential_path: Annotated[bool, Field(default=True)]
|
|
164
|
-
_pair_style_with_options:
|
|
182
|
+
_pair_style_with_options: List[str] = PrivateAttr(default=None)
|
|
165
183
|
|
|
166
184
|
|
|
167
185
|
reference_phase: Annotated[ str, Field(default = "")]
|
|
@@ -170,8 +188,8 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
170
188
|
Field(default=[1,1,1])]
|
|
171
189
|
|
|
172
190
|
script_mode: Annotated[ bool, Field(default = False)]
|
|
173
|
-
lammps_executable: Annotated[ str, Field(default = None)]
|
|
174
|
-
mpi_executable: Annotated[ str, Field(default = None)]
|
|
191
|
+
lammps_executable: Annotated[ Union[str,None], Field(default = None)]
|
|
192
|
+
mpi_executable: Annotated[ Union[str,None], Field(default = None)]
|
|
175
193
|
|
|
176
194
|
npt: Annotated[ bool, Field(default = True)]
|
|
177
195
|
n_equilibration_steps: Annotated[ int, Field(default = 25000)]
|
|
@@ -180,12 +198,11 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
180
198
|
_n_sweep_steps: int = PrivateAttr(default=50000)
|
|
181
199
|
n_print_steps: Annotated[int, Field(default = 0)]
|
|
182
200
|
n_iterations: Annotated[int, Field(default = 1)]
|
|
183
|
-
equilibration_control: Annotated[str, Field(default = None)]
|
|
184
|
-
folder_prefix: Annotated[str, Field(default = None)]
|
|
201
|
+
equilibration_control: Annotated[Union[str,None], Field(default = None)]
|
|
202
|
+
folder_prefix: Annotated[Union[str,None], Field(default = None)]
|
|
185
203
|
|
|
186
204
|
#add second level options; for example spring constants
|
|
187
|
-
spring_constants: Annotated[List[float], Field(default = None)]
|
|
188
|
-
_ghost_element_count: int = PrivateAttr(default=0)
|
|
205
|
+
spring_constants: Annotated[Union[List[float],None], Field(default = None)]
|
|
189
206
|
|
|
190
207
|
#structure items
|
|
191
208
|
_structure: Any = PrivateAttr(default=None)
|
|
@@ -312,6 +329,7 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
312
329
|
self._element_dict[element]['mass'] = self.mass[count]
|
|
313
330
|
self._element_dict[element]['count'] = 0
|
|
314
331
|
self._element_dict[element]['composition'] = 0.0
|
|
332
|
+
self._element_dict[element]['atomic_number'] = mendeleev.element(element).atomic_number
|
|
315
333
|
|
|
316
334
|
#generate temporary filename if needed
|
|
317
335
|
write_structure_file = False
|
|
@@ -319,7 +337,7 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
319
337
|
if self.lattice == "":
|
|
320
338
|
#fetch from dict
|
|
321
339
|
if len(self.element) > 1:
|
|
322
|
-
raise ValueError("Cannot create lattice for more than one element")
|
|
340
|
+
raise ValueError("Cannot create lattice for more than one element, provide a lammps-data file explicitly")
|
|
323
341
|
if self.element[0] in element_dict.keys():
|
|
324
342
|
self.lattice = element_dict[self.element[0]]['structure']
|
|
325
343
|
self.lattice_constant = element_dict[self.element[0]]['lattice_constant']
|
|
@@ -333,20 +351,23 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
333
351
|
lattice_constant=self.lattice_constant,
|
|
334
352
|
repetitions=self.repeat,
|
|
335
353
|
element=self.element)
|
|
354
|
+
structure = structure.write.ase()
|
|
336
355
|
|
|
337
356
|
#extract composition
|
|
338
|
-
|
|
339
|
-
types, typecounts = np.unique(typelist, return_counts=True)
|
|
357
|
+
types, typecounts = np.unique(structure.get_chemical_symbols(), return_counts=True)
|
|
340
358
|
|
|
341
359
|
for c, t in enumerate(types):
|
|
342
360
|
self._element_dict[t]['count'] = typecounts[c]
|
|
343
361
|
self._element_dict[t]['composition'] = typecounts[c]/np.sum(typecounts)
|
|
344
362
|
|
|
345
|
-
self._natoms = structure
|
|
363
|
+
self._natoms = len(structure)
|
|
346
364
|
self._original_lattice = self.lattice.lower()
|
|
347
365
|
write_structure_file = True
|
|
348
366
|
|
|
349
367
|
elif self.lattice.lower() in structure_dict.keys():
|
|
368
|
+
if len(self.element) > 1:
|
|
369
|
+
raise ValueError("Cannot create lattice for more than one element, provide a lammps-data file explicitly")
|
|
370
|
+
|
|
350
371
|
#this is a valid structure
|
|
351
372
|
if self.lattice_constant == 0:
|
|
352
373
|
#we try try to get lattice_constant
|
|
@@ -359,10 +380,10 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
359
380
|
lattice_constant=self.lattice_constant,
|
|
360
381
|
repetitions=self.repeat,
|
|
361
382
|
element=self.element)
|
|
383
|
+
structure = structure.write.ase()
|
|
362
384
|
|
|
363
385
|
#extract composition
|
|
364
|
-
|
|
365
|
-
types, typecounts = np.unique(typelist, return_counts=True)
|
|
386
|
+
types, typecounts = np.unique(structure.get_chemical_symbols(), return_counts=True)
|
|
366
387
|
|
|
367
388
|
for c, t in enumerate(types):
|
|
368
389
|
self._element_dict[t]['count'] = typecounts[c]
|
|
@@ -372,7 +393,7 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
372
393
|
#concdict_frac = {str(t): typecounts[c]/np.sum(typecounts) for c, t in enumerate(types)}
|
|
373
394
|
#self._composition = concdict_frac
|
|
374
395
|
#self._composition_counts = concdict_counts
|
|
375
|
-
self._natoms = structure
|
|
396
|
+
self._natoms = len(structure)
|
|
376
397
|
self._original_lattice = self.lattice.lower()
|
|
377
398
|
write_structure_file = True
|
|
378
399
|
|
|
@@ -381,20 +402,21 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
381
402
|
if not os.path.exists(self.lattice):
|
|
382
403
|
raise ValueError(f'File {self.lattice} could not be found')
|
|
383
404
|
if self.file_format == 'lammps-data':
|
|
384
|
-
|
|
385
|
-
|
|
405
|
+
#create atomic numbers for proper reading
|
|
406
|
+
Z_of_type = dict([(count+1, self._element_dict[element]['atomic_number']) for count, element in enumerate(self.element)])
|
|
407
|
+
structure = read(self.lattice, format='lammps-data', style='atomic', Z_of_type=Z_of_type)
|
|
408
|
+
#structure = System(aseobj, format='ase')
|
|
386
409
|
else:
|
|
387
410
|
raise TypeError('Only lammps-data files are supported!')
|
|
388
411
|
|
|
389
412
|
#extract composition
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
typelist = [self.element[x-1] for x in typelist]
|
|
393
|
-
types, typecounts = np.unique(typelist, return_counts=True)
|
|
413
|
+
#this is the types read in from the file
|
|
414
|
+
types, typecounts = np.unique(structure.get_chemical_symbols(), return_counts=True)
|
|
394
415
|
for c, t in enumerate(types):
|
|
395
416
|
self._element_dict[t]['count'] = typecounts[c]
|
|
396
417
|
self._element_dict[t]['composition'] = typecounts[c]/np.sum(typecounts)
|
|
397
|
-
|
|
418
|
+
|
|
419
|
+
self._natoms = len(structure)
|
|
398
420
|
self._original_lattice = os.path.basename(self.lattice)
|
|
399
421
|
self.lattice = os.path.abspath(self.lattice)
|
|
400
422
|
|
|
@@ -402,13 +424,10 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
402
424
|
if write_structure_file:
|
|
403
425
|
structure_filename = ".".join([self.create_identifier(), str(self.kernel), "data"])
|
|
404
426
|
structure_filename = os.path.join(os.getcwd(), structure_filename)
|
|
405
|
-
|
|
427
|
+
write(structure_filename, structure, format='lammps-data')
|
|
406
428
|
self.lattice = structure_filename
|
|
407
429
|
|
|
408
430
|
if self.mode == 'composition_scaling':
|
|
409
|
-
aseobj = read(self.lattice, format='lammps-data', style='atomic')
|
|
410
|
-
structure = System(aseobj, format='ase')
|
|
411
|
-
|
|
412
431
|
#we also should check if actual contents are present
|
|
413
432
|
input_chem_comp = {}
|
|
414
433
|
for key, val in self._element_dict.items():
|
|
@@ -422,7 +441,7 @@ class Calculation(BaseModel, title='Main input class'):
|
|
|
422
441
|
|
|
423
442
|
natoms1 = np.sum([val for key, val in self.composition_scaling._input_chemical_composition.items()])
|
|
424
443
|
natoms2 = np.sum([val for key, val in self.composition_scaling.output_chemical_composition.items()])
|
|
425
|
-
if not (natoms1==natoms2
|
|
444
|
+
if not (natoms1==natoms2):
|
|
426
445
|
raise ValueError(f"Input and output number of atoms are not conserved! Input {self.dict_to_string(self.input_chemical_composition)}, output {self.dict_to_string(self.output_chemical_composition)}, total atoms in structure {structure.natoms}")
|
|
427
446
|
return self
|
|
428
447
|
|
|
@@ -517,13 +536,6 @@ def load_job(filename):
|
|
|
517
536
|
job = np.load(filename, allow_pickle=True).flatten()[0]
|
|
518
537
|
return job
|
|
519
538
|
|
|
520
|
-
def check_dict(indict, key, retval=None):
|
|
521
|
-
if key in indict.items():
|
|
522
|
-
return indict[key]
|
|
523
|
-
else:
|
|
524
|
-
return retval
|
|
525
|
-
|
|
526
|
-
|
|
527
539
|
def read_inputfile(file):
|
|
528
540
|
if not os.path.exists(file):
|
|
529
541
|
raise FileNotFoundError(f'Input file {file} not found.')
|
|
@@ -648,20 +660,18 @@ def _convert_legacy_inputfile(file, return_calcs=False):
|
|
|
648
660
|
return outfile
|
|
649
661
|
|
|
650
662
|
|
|
651
|
-
def
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
663
|
+
def generate_metadata():
|
|
664
|
+
metadata = {}
|
|
665
|
+
metadata["software"] = {}
|
|
666
|
+
metadata["software"]["name"] = "calphy"
|
|
667
|
+
metadata["software"]["doi"] = "10.5281/zenodo.10527452"
|
|
668
|
+
metadata["software"]["version"] = __version__
|
|
669
|
+
metadata["software"]["repository"] = "https://github.com/ICAMS/calphy"
|
|
670
|
+
metadata["software"]["webpage"] = "https://calphy.org/"
|
|
656
671
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
return [int(x) for x in val]
|
|
672
|
+
metadata["files"] = {}
|
|
673
|
+
metadata["files"]["input_file.yml"] = "input file"
|
|
674
|
+
metadata["files"]["report.yaml"] = "results after thermodynamic integration"
|
|
675
|
+
metadata["files"]["input_configuration.data"] = "input atomic configuration"
|
|
662
676
|
|
|
663
|
-
|
|
664
|
-
if np.isscalar(val):
|
|
665
|
-
return float(val)
|
|
666
|
-
else:
|
|
667
|
-
return [float(x) for x in val]
|
|
677
|
+
return metadata
|