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.
Files changed (38) hide show
  1. {calphy-1.2.14/calphy.egg-info → calphy-1.3.2}/PKG-INFO +1 -2
  2. {calphy-1.2.14 → calphy-1.3.2}/calphy/__init__.py +1 -1
  3. {calphy-1.2.14 → calphy-1.3.2}/calphy/alchemy.py +9 -4
  4. {calphy-1.2.14 → calphy-1.3.2}/calphy/helpers.py +20 -190
  5. {calphy-1.2.14 → calphy-1.3.2}/calphy/input.py +72 -62
  6. {calphy-1.2.14 → calphy-1.3.2}/calphy/integrators.py +61 -34
  7. {calphy-1.2.14 → calphy-1.3.2}/calphy/liquid.py +11 -5
  8. {calphy-1.2.14 → calphy-1.3.2}/calphy/phase.py +31 -76
  9. {calphy-1.2.14 → calphy-1.3.2}/calphy/routines.py +5 -0
  10. {calphy-1.2.14 → calphy-1.3.2}/calphy/scheduler.py +2 -3
  11. {calphy-1.2.14 → calphy-1.3.2}/calphy/solid.py +37 -18
  12. {calphy-1.2.14 → calphy-1.3.2/calphy.egg-info}/PKG-INFO +1 -2
  13. {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/SOURCES.txt +0 -4
  14. {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/requires.txt +0 -1
  15. {calphy-1.2.14 → calphy-1.3.2}/setup.py +2 -2
  16. {calphy-1.2.14 → calphy-1.3.2}/tests/test_integrators.py +0 -4
  17. {calphy-1.2.14 → calphy-1.3.2}/tests/test_solid_methods.py +0 -1
  18. calphy-1.2.14/calphy/inputbk.py +0 -862
  19. calphy-1.2.14/calphy/lattice.py +0 -180
  20. calphy-1.2.14/tests/test_liquid_avg.py +0 -33
  21. calphy-1.2.14/tests/test_solid_avg.py +0 -34
  22. {calphy-1.2.14 → calphy-1.3.2}/LICENSE +0 -0
  23. {calphy-1.2.14 → calphy-1.3.2}/MANIFEST.in +0 -0
  24. {calphy-1.2.14 → calphy-1.3.2}/README.md +0 -0
  25. {calphy-1.2.14 → calphy-1.3.2}/calphy/clitools.py +0 -0
  26. {calphy-1.2.14 → calphy-1.3.2}/calphy/composition_transformation.py +0 -0
  27. {calphy-1.2.14 → calphy-1.3.2}/calphy/errors.py +0 -0
  28. {calphy-1.2.14 → calphy-1.3.2}/calphy/kernel.py +0 -0
  29. {calphy-1.2.14 → calphy-1.3.2}/calphy/phase_diagram.py +0 -0
  30. {calphy-1.2.14 → calphy-1.3.2}/calphy/queuekernel.py +0 -0
  31. {calphy-1.2.14 → calphy-1.3.2}/calphy/splines.py +0 -0
  32. {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/dependency_links.txt +0 -0
  33. {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/entry_points.txt +0 -0
  34. {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/not-zip-safe +0 -0
  35. {calphy-1.2.14 → calphy-1.3.2}/calphy.egg-info/top_level.txt +0 -0
  36. {calphy-1.2.14 → calphy-1.3.2}/setup.cfg +0 -0
  37. {calphy-1.2.14 → calphy-1.3.2}/tests/test_helpers.py +0 -0
  38. {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.14
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
@@ -4,7 +4,7 @@ from calphy.solid import Solid
4
4
  from calphy.alchemy import Alchemy
5
5
  from calphy.routines import MeltingTemp
6
6
 
7
- __version__ = "1.2.14"
7
+ __version__ = "1.3.2"
8
8
 
9
9
  def addtest(a,b):
10
10
  return a+b
@@ -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 = ph.set_potential(lmp, self.calc, ghost_elements=self.calc._ghost_element_count)
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 = ph.set_potential(lmp, self.calc, ghost_elements=self.calc._ghost_element_count)
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
- from pylammpsmpi import LammpsLibrary
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
- from pyscal.trajectory import Trajectory
34
- import warnings
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=None,
49
- init_commands=None, script_mode=False):
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 is not None:
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, ghost_elements=0):
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, ghost_elements=0):
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, ghost_elements=ghost_elements)
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.find_neighbors(method="cutoff", cutoff=0)
207
+ sys.find.neighbors(method="cutoff", cutoff=0)
336
208
  except RuntimeError:
337
- sys.find_neighbors(
209
+ sys.find.neighbors(
338
210
  method="cutoff", cutoff=5.0
339
211
  ) # Maybe add value as convergence param?
340
- solids = sys.find_solids()
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=None, required=False)]
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=None)]
82
- init_commands: Annotated[str, Field(default=None)]
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=None)]
98
- queuename: Annotated[str, Field(default=None)]
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[str], Field(default=None)]
101
- options: Annotated[List[str], Field(default=None)]
102
- modules: Annotated[List[str], Field(default=None)]
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: float = PrivateAttr(default=None)
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
- typelist = structure.atoms.species
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.natoms
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
- typelist = structure.atoms.species
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.natoms
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
- aseobj = read(self.lattice, format='lammps-data', style='atomic')
385
- structure = System(aseobj, format='ase')
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
- typelist = structure.atoms.types
391
- #convert to species
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
- self._natoms = structure.natoms
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
- structure.write.file(structure_filename, format='lammps-data')
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==structure.natoms):
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 _to_str(val):
652
- if np.isscalar(val):
653
- return str(val)
654
- else:
655
- return [str(x) for x in val]
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
- def _to_int(val):
658
- if np.isscalar(val):
659
- return int(val)
660
- else:
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
- def _to_float(val):
664
- if np.isscalar(val):
665
- return float(val)
666
- else:
667
- return [float(x) for x in val]
677
+ return metadata