calphy 1.4.5__py3-none-any.whl → 1.4.12__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/phase_diagram.py CHANGED
@@ -254,6 +254,142 @@ matcolors = {
254
254
  }
255
255
  }
256
256
 
257
+ def read_structure_composition(lattice_file, element_list):
258
+ """
259
+ Read a LAMMPS data file and determine the input chemical composition.
260
+
261
+ Parameters
262
+ ----------
263
+ lattice_file : str
264
+ Path to the LAMMPS data file
265
+ element_list : list
266
+ List of element symbols in order (element[0] = type 1, element[1] = type 2, etc.)
267
+
268
+ Returns
269
+ -------
270
+ dict
271
+ Dictionary mapping element symbols to atom counts
272
+ Elements not present in the structure will have count 0
273
+ """
274
+ from ase.io import read
275
+ from collections import Counter
276
+
277
+ # Read the structure file
278
+ structure = read(lattice_file, format='lammps-data', style='atomic')
279
+
280
+ # Get the species/types from the structure
281
+ # ASE reads LAMMPS types as species strings ('1', '2', etc.)
282
+ if 'species' in structure.arrays:
283
+ types_in_structure = structure.arrays['species']
284
+ else:
285
+ # Fallback: get atomic numbers and convert to strings
286
+ types_in_structure = [str(x) for x in structure.get_atomic_numbers()]
287
+
288
+ # Count atoms by type
289
+ type_counts = Counter(types_in_structure)
290
+
291
+ # Build composition mapping element names to counts
292
+ # element[0] corresponds to LAMMPS type '1', element[1] to type '2', etc.
293
+ input_chemical_composition = {}
294
+ for idx, element in enumerate(element_list):
295
+ lammps_type = str(idx + 1) # LAMMPS types are 1-indexed
296
+ input_chemical_composition[element] = type_counts.get(lammps_type, 0)
297
+
298
+ return input_chemical_composition
299
+
300
+
301
+ # Constants for phase diagram preparation
302
+ COMPOSITION_TOLERANCE = 1E-5
303
+
304
+
305
+ def _create_composition_array(comp_range, interval, reference):
306
+ """
307
+ Create composition array from range specification.
308
+
309
+ Parameters
310
+ ----------
311
+ comp_range : list or scalar
312
+ Composition range [min, max] or single value
313
+ interval : float
314
+ Composition interval
315
+ reference : float
316
+ Reference composition value
317
+
318
+ Returns
319
+ -------
320
+ tuple
321
+ (comp_arr, is_reference) - composition array and boolean array marking reference compositions
322
+ """
323
+ # Convert to list if scalar
324
+ if not isinstance(comp_range, list):
325
+ comp_range = [comp_range]
326
+
327
+ if len(comp_range) == 2:
328
+ comp_arr = np.arange(comp_range[0], comp_range[-1], interval)
329
+ last_val = comp_range[-1]
330
+ if last_val not in comp_arr:
331
+ comp_arr = np.append(comp_arr, last_val)
332
+ is_reference = np.abs(comp_arr - reference) < COMPOSITION_TOLERANCE
333
+ elif len(comp_range) == 1:
334
+ comp_arr = [comp_range[0]]
335
+ # Check if this single composition equals the reference
336
+ is_reference = [np.abs(comp_range[0] - reference) < COMPOSITION_TOLERANCE]
337
+ else:
338
+ raise ValueError("Composition range should be scalar or list of two values!")
339
+
340
+ return comp_arr, is_reference
341
+
342
+
343
+ def _create_temperature_array(temp_range, interval):
344
+ """
345
+ Create temperature array from range specification.
346
+
347
+ Parameters
348
+ ----------
349
+ temp_range : list or scalar
350
+ Temperature range [min, max] or single value
351
+ interval : float
352
+ Temperature interval
353
+
354
+ Returns
355
+ -------
356
+ ndarray
357
+ Temperature array
358
+ """
359
+ # Convert to list if scalar
360
+ if not isinstance(temp_range, list):
361
+ temp_range = [temp_range]
362
+
363
+ if len(temp_range) == 2:
364
+ ntemps = int((temp_range[-1] - temp_range[0]) / interval) + 1
365
+ temp_arr = np.linspace(temp_range[0], temp_range[-1], ntemps, endpoint=True)
366
+ elif len(temp_range) == 1:
367
+ temp_arr = [temp_range[0]]
368
+ else:
369
+ raise ValueError("Temperature range should be scalar or list of two values!")
370
+
371
+ return temp_arr
372
+
373
+
374
+ def _add_temperature_calculations(calc_dict, temp_arr, all_calculations):
375
+ """
376
+ Helper to add calculations for each temperature point.
377
+
378
+ Parameters
379
+ ----------
380
+ calc_dict : dict
381
+ Base calculation dictionary
382
+ temp_arr : array
383
+ Array of temperatures
384
+ all_calculations : list
385
+ List to append calculations to
386
+ """
387
+ for temp in temp_arr:
388
+ calc_for_temp = copy.deepcopy(calc_dict)
389
+ calc_for_temp['temperature'] = int(temp)
390
+ all_calculations.append(calc_for_temp)
391
+
392
+
257
393
  def fix_data_file(datafile, nelements):
258
394
  """
259
395
  Change the atom types keyword in the structure file
@@ -309,6 +445,31 @@ def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
309
445
  calculation_base_name = inputyamlfile
310
446
 
311
447
  for phase in data['phases']:
448
+ # Validate binary system assumption
449
+ n_elements = len(phase['element'])
450
+ if n_elements != 2:
451
+ raise ValueError(
452
+ f"Phase diagram preparation currently supports only binary systems. "
453
+ f"Found {n_elements} elements: {phase['element']}"
454
+ )
455
+
456
+ # Validate element ordering consistency with pair_coeff
457
+ # This ensures element[0] -> type 1, element[1] -> type 2
458
+ if 'pair_coeff' in phase:
459
+ from calphy.input import _extract_elements_from_pair_coeff
460
+ # pair_coeff can be a list or a string - handle both
461
+ pair_coeff = phase['pair_coeff']
462
+ if isinstance(pair_coeff, list):
463
+ pair_coeff = pair_coeff[0] if pair_coeff else None
464
+ pair_coeff_elements = _extract_elements_from_pair_coeff(pair_coeff)
465
+ if pair_coeff_elements != phase['element']:
466
+ raise ValueError(
467
+ f"Element ordering mismatch for phase '{phase.get('phase_name', 'unnamed')}'!\n"
468
+ f"Elements in 'element' field: {phase['element']}\n"
469
+ f"Elements from pair_coeff: {pair_coeff_elements}\n"
470
+ f"These must match exactly in order (element[0] -> LAMMPS type 1, element[1] -> type 2)."
471
+ )
472
+
312
473
  phase_reference_state = phase['reference_phase']
313
474
  phase_name = phase['phase_name']
314
475
 
@@ -325,34 +486,18 @@ def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
325
486
  other_element_list.remove(reference_element)
326
487
  other_element = other_element_list[0]
327
488
 
328
- #convert to list if scalar
329
- if not isinstance(comps['range'], list):
330
- comps["range"] = [comps["range"]]
331
- if len(comps["range"]) == 2:
332
- comp_arr = np.arange(comps['range'][0], comps['range'][-1], comps['interval'])
333
- last_val = comps['range'][-1]
334
- if last_val not in comp_arr:
335
- comp_arr = np.append(comp_arr, last_val)
336
- ncomps = len(comp_arr)
337
- is_reference = np.abs(comp_arr-comps['reference']) < 1E-5
338
- elif len(comps["range"]) == 1:
339
- ncomps = 1
340
- comp_arr = [comps["range"][0]]
341
- is_reference = [True]
342
- else:
343
- raise ValueError("Composition range should be scalar of list of two values!")
489
+ # Create composition array using helper function
490
+ comp_arr, is_reference = _create_composition_array(
491
+ comps['range'],
492
+ comps['interval'],
493
+ comps['reference']
494
+ )
495
+ ncomps = len(comp_arr)
344
496
 
497
+ # Create temperature array using helper function
345
498
  temps = phase["temperature"]
346
- if not isinstance(temps['range'], list):
347
- temps["range"] = [temps["range"]]
348
- if len(temps["range"]) == 2:
349
- ntemps = int((temps['range'][-1]-temps['range'][0])/temps['interval'])+1
350
- temp_arr = np.linspace(temps['range'][0], temps['range'][-1], ntemps, endpoint=True)
351
- elif len(temps["range"]) == 1:
352
- ntemps = 1
353
- temp_arr = [temps["range"][0]]
354
- else:
355
- raise ValueError("Temperature range should be scalar of list of two values!")
499
+ temp_arr = _create_temperature_array(temps['range'], temps['interval'])
500
+ ntemps = len(temp_arr)
356
501
 
357
502
  all_calculations = []
358
503
 
@@ -372,27 +517,30 @@ def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
372
517
  outfile = fix_data_file(calc['lattice'], len(calc['element']))
373
518
 
374
519
  #add ref phase, needed
375
- calc['reference_phase'] = str(phase_reference_state)
520
+ calc['reference_phase'] = phase_reference_state
376
521
  calc['reference_composition'] = comps['reference']
377
- calc['mode'] = str('fe')
522
+ calc['mode'] = 'fe'
378
523
  calc['folder_prefix'] = f'{phase_name}-{comp:.2f}'
379
- calc['lattice'] = str(outfile)
524
+ calc['lattice'] = outfile
380
525
 
381
- #now we need to run this for different temp
382
- for temp in temp_arr:
383
- calc_for_temp = copy.deepcopy(calc)
384
- calc_for_temp['temperature'] = int(temp)
385
- all_calculations.append(calc_for_temp)
526
+ # Add calculations for each temperature
527
+ _add_temperature_calculations(calc, temp_arr, all_calculations)
386
528
  else:
387
529
  #off stoichiometric
388
530
  #copy the dict
389
531
  calc = copy.deepcopy(phase)
390
532
 
391
- #first thing first, we need to calculate the number of atoms
392
- #we follow the convention that composition is always given with the second species
393
- n_atoms = np.sum(calc['composition']['number_of_atoms'])
533
+ #read the structure file to determine input composition automatically
534
+ input_chemical_composition = read_structure_composition(calc['lattice'], calc['element'])
535
+
536
+ #calculate total number of atoms from structure
537
+ n_atoms = sum(input_chemical_composition.values())
538
+
539
+ if n_atoms == 0:
540
+ raise ValueError(f"No atoms found in structure file {calc['lattice']}")
394
541
 
395
- #find number of atoms of second species
542
+ #find number of atoms of second species based on target composition
543
+ #we follow the convention that composition is always given with the reference element
396
544
  output_chemical_composition = {}
397
545
  n_species_b = int(np.round(comp*n_atoms, decimals=0))
398
546
  output_chemical_composition[reference_element] = n_species_b
@@ -400,11 +548,8 @@ def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
400
548
  n_species_a = int(n_atoms-n_species_b)
401
549
  output_chemical_composition[other_element] = n_species_a
402
550
 
403
- if n_species_a == 0:
404
- raise ValueError("Please add pure phase as a new entry!")
405
- #create input comp dict and output comp dict
406
- input_chemical_composition = {element:number for element, number in zip(calc['element'],
407
- calc['composition']['number_of_atoms'])}
551
+ # Note: Pure phases (n_species_a == 0 or n_species_b == 0) are allowed
552
+ # Composition transformation can handle 100% replacement
408
553
 
409
554
  #good, now we need to write such a structure out; likely better to use working directory for that
410
555
  folder_prefix = f'{phase_name}-{comp:.2f}'
@@ -421,7 +566,7 @@ def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
421
566
 
422
567
  #just submit comp scales
423
568
  #add ref phase, needed
424
- calc['mode'] = str('composition_scaling')
569
+ calc['mode'] = 'composition_scaling'
425
570
  calc['folder_prefix'] = folder_prefix
426
571
  calc['composition_scaling'] = {}
427
572
  calc['composition_scaling']['output_chemical_composition'] = output_chemical_composition
@@ -447,22 +592,20 @@ def prepare_inputs_for_phase_diagram(inputyamlfile, calculation_base_name=None):
447
592
  _ = calc.pop(key, None)
448
593
 
449
594
  #add ref phase, needed
450
- calc['mode'] = str('fe')
595
+ calc['mode'] = 'fe'
451
596
  calc['folder_prefix'] = folder_prefix
452
- calc['lattice'] = str(outfile)
597
+ calc['lattice'] = outfile
453
598
 
454
- #now we need to run this for different temp
455
- for temp in temp_arr:
456
- calc_for_temp = copy.deepcopy(calc)
457
- calc_for_temp['temperature'] = int(temp)
458
- all_calculations.append(calc_for_temp)
599
+ # Add calculations for each temperature
600
+ _add_temperature_calculations(calc, temp_arr, all_calculations)
459
601
 
460
602
  #finish and write up the file
461
603
  output_data = {"calculations": all_calculations}
604
+ base_name = os.path.basename(calculation_base_name)
462
605
  for rep in ['.yml', '.yaml']:
463
- calculation_base_name = calculation_base_name.replace(rep, '')
606
+ base_name = base_name.replace(rep, '')
464
607
 
465
- outfile_phase = phase_name + '_' + calculation_base_name + ".yaml"
608
+ outfile_phase = phase_name + '_' + base_name + ".yaml"
466
609
  with open(outfile_phase, 'w') as fout:
467
610
  yaml.safe_dump(output_data, fout)
468
611
  print(f'Total {len(all_calculations)} calculations found for phase {phase_name}, written to {outfile_phase}')
calphy/routines.py CHANGED
@@ -94,6 +94,9 @@ class MeltingTemp:
94
94
  calc["mode"] = "ts"
95
95
  calc["temperature"] = [int(self.tmin), int(self.tmax)]
96
96
  calc["reference_phase"] = 'solid'
97
+ # Preserve n_iterations from the original melting_temperature calculation
98
+ if "n_iterations" in data["calculations"][int(self.calc.kernel)]:
99
+ calc["n_iterations"] = data["calculations"][int(self.calc.kernel)]["n_iterations"]
97
100
  calculations["calculations"].append(calc)
98
101
 
99
102
  with open(self.calc.inputfile, 'r') as fin:
@@ -103,6 +106,9 @@ class MeltingTemp:
103
106
  calc["mode"] = "ts"
104
107
  calc["temperature"] = [int(self.tmin), int(self.tmax)]
105
108
  calc["reference_phase"] = 'liquid'
109
+ # Preserve n_iterations from the original melting_temperature calculation
110
+ if "n_iterations" in data["calculations"][int(self.calc.kernel)]:
111
+ calc["n_iterations"] = data["calculations"][int(self.calc.kernel)]["n_iterations"]
106
112
  calculations["calculations"].append(calc)
107
113
 
108
114
  outfile = f'{self.calc.create_identifier()}.{self.attempts}.yaml'
calphy/scheduler.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.
@@ -25,25 +25,26 @@ import subprocess as sub
25
25
  import os
26
26
  import stat
27
27
 
28
+
28
29
  class Local:
29
30
  """
30
31
  Local submission script
31
32
  """
33
+
32
34
  def __init__(self, options, cores=1, directory=os.getcwd()):
33
- self.queueoptions = {"scheduler": "local",
34
- "jobname": "tis",
35
- "queuename": None,
36
- "memory": None,
37
- "cores": cores,
38
- "hint": None,
39
- "directory": directory,
40
- "options": [],
41
- "commands": [],
42
- "modules": [],
43
- "header": "#!/bin/bash"
44
-
45
- }
46
- for (key, val) in options.items():
35
+ self.queueoptions = {
36
+ "scheduler": "local",
37
+ "jobname": "tis",
38
+ "queuename": None,
39
+ "memory": None,
40
+ "cores": cores,
41
+ "hint": None,
42
+ "directory": directory,
43
+ "options": [],
44
+ "commands": [],
45
+ "header": "#!/bin/bash",
46
+ }
47
+ for key, val in options.items():
47
48
  if key in self.queueoptions.keys():
48
49
  if val is not None:
49
50
  self.queueoptions[key] = val
@@ -60,14 +61,10 @@ class Local:
60
61
  fout.write(self.queueoptions["header"])
61
62
  fout.write("\n")
62
63
 
63
- #now write modules
64
- for module in self.queueoptions["modules"]:
65
- fout.write("module load %s\n" %module)
66
-
67
- #now finally commands
64
+ # now finally commands
68
65
  for command in self.queueoptions["commands"]:
69
- fout.write("%s\n" %command)
70
- fout.write("%s > %s 2> %s\n" %(self.maincommand, jobout, joberr))
66
+ fout.write("%s\n" % command)
67
+ fout.write("%s > %s 2> %s\n" % (self.maincommand, jobout, joberr))
71
68
  self.script = outfile
72
69
 
73
70
  def submit(self):
@@ -77,41 +74,42 @@ class Local:
77
74
  st = os.stat(self.script)
78
75
  os.chmod(self.script, st.st_mode | stat.S_IEXEC)
79
76
  cmd = [self.script]
80
- proc = sub.Popen(cmd, stdin=sub.PIPE,stdout=sub.PIPE,stderr=sub.PIPE)
77
+ proc = sub.Popen(cmd, stdin=sub.PIPE, stdout=sub.PIPE, stderr=sub.PIPE)
81
78
  return proc
82
79
 
80
+
83
81
  class SLURM:
84
82
  """
85
83
  Slurm class for writing submission script
86
84
  """
85
+
87
86
  def __init__(self, options, cores=1, directory=os.getcwd()):
88
87
  """
89
88
  Create class
90
89
  """
91
- self.queueoptions = {"scheduler": "slurm",
92
- "jobname": "tis",
93
- "queuename": None,
94
- "walltime": "23:59:00",
95
- "memory": "3GB",
96
- "cores": cores,
97
- "hint": "nomultithread",
98
- "directory": directory,
99
- "options": [],
100
- "commands": [ "uss=$(whoami)",
101
- "find /dev/shm/ -user $uss -type f -mmin +30 -delete",
102
- ],
103
- "modules": [],
104
- "header": "#!/bin/bash"
105
-
106
- }
107
- for (key, val) in options.items():
90
+ self.queueoptions = {
91
+ "scheduler": "slurm",
92
+ "jobname": "tis",
93
+ "queuename": None,
94
+ "walltime": "23:59:00",
95
+ "memory": "3GB",
96
+ "cores": cores,
97
+ "hint": "nomultithread",
98
+ "directory": directory,
99
+ "options": [],
100
+ "commands": [
101
+ "uss=$(whoami)",
102
+ "find /dev/shm/ -user $uss -type f -mmin +30 -delete",
103
+ ],
104
+ "header": "#!/bin/bash",
105
+ }
106
+ for key, val in options.items():
108
107
  if key in self.queueoptions.keys():
109
108
  if val is not None:
110
109
  if val != "":
111
110
  self.queueoptions[key] = val
112
111
  self.maincommand = ""
113
112
 
114
-
115
113
  def write_script(self, outfile):
116
114
  """
117
115
  Write the script file
@@ -123,28 +121,24 @@ class SLURM:
123
121
  fout.write(self.queueoptions["header"])
124
122
  fout.write("\n")
125
123
 
126
- #write the main header options
127
- fout.write("#SBATCH --job-name=%s\n" %self.queueoptions["jobname"])
128
- fout.write("#SBATCH --time=%s\n" %self.queueoptions["walltime"])
124
+ # write the main header options
125
+ fout.write("#SBATCH --job-name=%s\n" % self.queueoptions["jobname"])
126
+ fout.write("#SBATCH --time=%s\n" % self.queueoptions["walltime"])
129
127
  if self.queueoptions["queuename"] is not None:
130
- fout.write("#SBATCH --partition=%s\n"%self.queueoptions["queuename"])
131
- fout.write("#SBATCH --ntasks=%s\n" %str(self.queueoptions["cores"]))
132
- fout.write("#SBATCH --mem-per-cpu=%s\n"%self.queueoptions["memory"])
133
- fout.write("#SBATCH --hint=%s\n" %self.queueoptions["hint"])
134
- fout.write("#SBATCH --chdir=%s\n" %self.queueoptions["directory"])
128
+ fout.write("#SBATCH --partition=%s\n" % self.queueoptions["queuename"])
129
+ fout.write("#SBATCH --ntasks=%s\n" % str(self.queueoptions["cores"]))
130
+ fout.write("#SBATCH --mem-per-cpu=%s\n" % self.queueoptions["memory"])
131
+ fout.write("#SBATCH --hint=%s\n" % self.queueoptions["hint"])
132
+ fout.write("#SBATCH --chdir=%s\n" % self.queueoptions["directory"])
135
133
 
136
- #now write extra options
134
+ # now write extra options
137
135
  for option in self.queueoptions["options"]:
138
- fout.write("#SBATCH %s\n" %option)
136
+ fout.write("#SBATCH %s\n" % option)
139
137
 
140
- #now write modules
141
- for module in self.queueoptions["modules"]:
142
- fout.write("module load %s\n" %module)
143
-
144
- #now finally commands
138
+ # now finally commands
145
139
  for command in self.queueoptions["commands"]:
146
- fout.write("%s\n" %command)
147
- fout.write("%s > %s 2> %s\n" %(self.maincommand, jobout, joberr))
140
+ fout.write("%s\n" % command)
141
+ fout.write("%s > %s 2> %s\n" % (self.maincommand, jobout, joberr))
148
142
 
149
143
  self.script = outfile
150
144
 
@@ -152,40 +146,41 @@ class SLURM:
152
146
  """
153
147
  Submit the job
154
148
  """
155
- cmd = ['sbatch', self.script]
156
- proc = sub.Popen(cmd, stdin=sub.PIPE,stdout=sub.PIPE,stderr=sub.PIPE)
149
+ cmd = ["sbatch", self.script]
150
+ proc = sub.Popen(cmd, stdin=sub.PIPE, stdout=sub.PIPE, stderr=sub.PIPE)
157
151
  print(f'submitting {self.queueoptions["jobname"]}')
158
152
  proc.communicate()
159
153
  return proc
160
154
 
161
155
 
162
-
163
156
  class SGE:
164
157
  """
165
158
  Slurm class for writing submission script
166
159
  """
160
+
167
161
  def __init__(self, options, cores=1, directory=os.getcwd()):
168
162
  """
169
163
  Create class
170
164
  """
171
- self.queueoptions = {"scheduler": "sge",
172
- "jobname": "tis",
173
- "walltime": "23:59:00",
174
- "queuename": None,
175
- "memory": "3GB",
176
- "system": "smp",
177
- "commands": [],
178
- "modules": [],
179
- "options": ["-j y",
180
- "-R y",
181
- "-P ams.p",
182
- ],
183
- "cores": cores,
184
- "hint": None,
185
- "directory": directory,
186
- "header": "#!/bin/bash"
187
- }
188
- for (key, val) in options.items():
165
+ self.queueoptions = {
166
+ "scheduler": "sge",
167
+ "jobname": "tis",
168
+ "walltime": "23:59:00",
169
+ "queuename": None,
170
+ "memory": "3GB",
171
+ "system": "smp",
172
+ "commands": [],
173
+ "options": [
174
+ "-j y",
175
+ "-R y",
176
+ "-P ams.p",
177
+ ],
178
+ "cores": cores,
179
+ "hint": None,
180
+ "directory": directory,
181
+ "header": "#!/bin/bash",
182
+ }
183
+ for key, val in options.items():
189
184
  if key in self.queueoptions.keys():
190
185
  if val is not None:
191
186
  self.queueoptions[key] = val
@@ -195,40 +190,40 @@ class SGE:
195
190
  """
196
191
  Write the script file
197
192
  """
193
+ jobout = ".".join([outfile, "out"])
194
+ joberr = ".".join([outfile, "err"])
195
+
198
196
  with open(outfile, "w") as fout:
199
197
  fout.write(self.queueoptions["header"])
200
198
  fout.write("\n")
201
199
 
202
- #write the main header options
203
- fout.write("#$ -N %s\n" %self.queueoptions["jobname"])
204
- fout.write("#$ -l h_rt=%s\n" %self.queueoptions["walltime"])
205
- fout.write("#$ -l qname=%s\n"%self.queueoptions["queuename"])
206
- fout.write("#$ -pe %s %s\n" %( self.queueoptions["system"], str(self.queueoptions["cores"])))
207
- fout.write("#$ -l h_vmem=%s\n"%self.queueoptions["memory"])
208
- fout.write("#$ -cwd %s\n" %self.queueoptions["directory"])
209
-
210
- #now write extra options
200
+ # write the main header options
201
+ fout.write("#$ -N %s\n" % self.queueoptions["jobname"])
202
+ fout.write("#$ -l h_rt=%s\n" % self.queueoptions["walltime"])
203
+ fout.write("#$ -l qname=%s\n" % self.queueoptions["queuename"])
204
+ fout.write(
205
+ "#$ -pe %s %s\n"
206
+ % (self.queueoptions["system"], str(self.queueoptions["cores"]))
207
+ )
208
+ fout.write("#$ -l h_vmem=%s\n" % self.queueoptions["memory"])
209
+ fout.write("#$ -cwd %s\n" % self.queueoptions["directory"])
210
+
211
+ # now write extra options
211
212
  for option in self.queueoptions["options"]:
212
- fout.write("#$ %s\n" %option)
213
-
214
- #now write modules
215
- for module in self.queueoptions["modules"]:
216
- fout.write("module load %s\n" %module)
213
+ fout.write("#$ %s\n" % option)
217
214
 
218
- #now finally commands
215
+ # now finally commands
219
216
  for command in self.queueoptions["commands"]:
220
- fout.write("%s\n" %command)
217
+ fout.write("%s\n" % command)
221
218
 
222
- fout.write("%s > %s 2> %s\n" %(self.maincommand, jobout, joberr))
223
-
224
- self.script = outfile
219
+ fout.write("%s > %s 2> %s\n" % (self.maincommand, jobout, joberr))
225
220
 
221
+ self.script = outfile
226
222
 
227
223
  def submit(self):
228
224
  """
229
225
  Submit the job
230
226
  """
231
- cmd = ['qsub', self.script]
232
- proc = sub.Popen(cmd, stdin=sub.PIPE,stdout=sub.PIPE,stderr=sub.PIPE)
227
+ cmd = ["qsub", self.script]
228
+ proc = sub.Popen(cmd, stdin=sub.PIPE, stdout=sub.PIPE, stderr=sub.PIPE)
233
229
  return proc
234
-