PSCF v1.3
output.py
1"""! Module of parsers for PSCF output file formats. """
2
3import pscfpp.param as param
4import os
5
6# Contains classes Thermo, Species, State, and Sweep
7
8
179class Thermo:
180
181
191 def __init__(self, filename=None):
192 self.fHelmholtz = None
193 self.pressure = None
194 self.fIdeal = None
195 self.fInter = None
196 self.fExt = None
197 self.polymers = None
198 self.solvents = None
199 self.cellParams = None
200 self.tableLabel = None
201 self.homogeneous = None
202 self.homogeneousSpecies = None
203 self.stress = None
204 self.environmentStress = None
205
206 if filename != None:
207 with open(filename) as f:
208 self.read(f)
209
210
218 def read(self, file):
219 line = self.skipEmptyLine(file)
220
221 # Read fHelmholtz and pressure (required)
222 if line.startswith('fHelmholtz'):
223 self.fHelmholtz = float(line.split()[-1])
224 else:
225 raise Exception('Not valid Thermo file. No fHelmholtz found.')
226 line = file.readline()
227 if line.startswith('pressure'):
228 self.pressure = float(line.split()[-1])
229 else:
230 raise Exception('Not valid Thermo file. No pressure found.')
231 line = self.skipEmptyLine(file)
232
233 # Now, read all other blocks of thermo file in any order
234 hasPolymersBlock = False
235 while line != '':
236
237 # Read free energy contributions (optional)
238 if line.startswith('fIdeal'):
239 while line.strip() != '':
240 l = line.split()
241 if l[0] == 'fIdeal':
242 self.fIdeal = float(l[-1])
243 if l[0] == 'fInter':
244 self.fInter = float(l[-1])
245 if l[0] == 'fExt':
246 self.fExt = float(l[-1])
247 line = file.readline()
248 line = self.skipEmptyLine(file)
249
250 # Read polymers block
251 elif line.lower().startswith('polymers:'):
252 self.polymers = []
253 self.tableLabel = file.readline()
254 line = file.readline()
255 while line.strip() != '':
256 l = line.split()
257 self.polymers.append(Species(l))
258 line = file.readline()
259 line = self.skipEmptyLine(file)
260 hasPolymersBlock = True
261
262 # Read solvents block (optional)
263 elif line.lower().startswith('solvents:'):
264 self.solvents = []
265 line = file.readline()
266 line = file.readline()
267 while line.strip() != '':
268 l = line.split()
269 self.solvents.append(Species(l))
270 line = file.readline()
271 line = self.skipEmptyLine(file)
272
273 # Read lattice parameters (optional)
274 elif line.startswith('cellParams:'):
275 self.cellParams = []
276 line = file.readline()
277 while line.strip() != '':
278 l = line.split()
279 self.cellParams.append(float(l[1]))
280 line = file.readline()
281 line = self.skipEmptyLine(file)
282
283 # Read homogeneous block (optional, r1d only)
284 elif line.startswith('f (homo)'):
285 self.homogeneous = {}
286 self.homogeneousSpecies = []
287
288 while line.strip() != '':
289 self.homogeneous[line[:11].strip()] = float(line[12:31])
290 line = file.readline()
291
292 line = self.skipEmptyLine(file)
293 if line.lower().startswith('species:'):
294 line = file.readline()
295 speciesParams = line.split()
296 line = file.readline()
297 while line.strip() != '':
298 l = line.split()
299 self.homogeneousSpecies.append({})
300 for i in range(4):
301 prm = speciesParams[i]
302 if i == 0:
303 self.homogeneousSpecies[-1][prm] = int(l[i])
304 else:
305 self.homogeneousSpecies[-1][prm] = float(l[i])
306 line = file.readline()
307 line = self.skipEmptyLine(file)
308
309 # Read stress block (optional)
310 elif line.lower().startswith('stress:'):
311 self.stress = []
312 line = file.readline()
313 while line.strip() != '':
314 l = line.split()
315 self.stress.append(float(l[1]))
316 line = file.readline()
317 line = self.skipEmptyLine(file)
318
319 # Read environment stress block (optional)
320 elif line.lower().startswith('environment-modified stress:'):
321 self.environmentStress = []
322 line = file.readline()
323 while line.strip() != '':
324 l = line.split()
325 self.environmentStress.append(float(l[1]))
326 line = file.readline()
327 line = self.skipEmptyLine(file)
328
329 if not hasPolymersBlock:
330 raise Exception('Not valid Thermo file. No polymers block found.')
331
332
340 def skipEmptyLine(self, file):
341 line = file.readline()
342 while line == '\n':
343 line = file.readline()
344 return line
345
346
355 def write(self, filename):
356 with open(filename, 'w') as f:
357 f.write(self.__str__())
358
359
362 def __str__(self):
363 s = ''
364 s += 'fHelmholtz'
365 v = f'{self.fHelmholtz:.11e}'
366 s += f'{v:>22}\n'
367 s += 'pressure '
368 v = f'{self.pressure:.11e}'
369 s += f'{v:>22}\n'
370 s += '\n'
371
372 if (self.fIdeal != None) or (self.fInter != None) or (self.fExt != None):
373 if self.fIdeal != None:
374 s += 'fIdeal '
375 v = f'{self.fIdeal:.11e}'
376 s += f'{v:>22}\n'
377 if self.fInter != None:
378 s += 'fInter '
379 v = f'{self.fInter:.11e}'
380 s += f'{v:>22}\n'
381 if self.fExt != None:
382 s += 'fExt '
383 v = f'{self.fExt:.11e}'
384 s += f'{v:>22}\n'
385 s += '\n'
386
387 s += 'polymers:\n'
388 s += self.tableLabel
389 for i in range(len(self.polymers)):
390 p = f'{self.polymers[i].phi:.11e}'
391 m = f'{self.polymers[i].mu:.11e}'
392 s += f'{i:>5}{p:>20}{m:>20}\n'
393 s += '\n'
394
395 if self.solvents != None:
396 s += 'solvents:\n'
397 s += self.tableLabel
398 for i in range(len(self.solvents)):
399 p = f'{self.solvents[i].phi:.11e}'
400 m = f'{self.solvents[i].mu:.11e}'
401 s += f'{i:>5}{p:>20}{m:>20}\n'
402 s += '\n'
403
404 if self.cellParams != None:
405 s += 'cellParams:\n'
406 for i in range(len(self.cellParams)):
407 v = f'{self.cellParams[i]:.11e}'
408 s += f'{i:>5}{v:>20}\n'
409 s += '\n'
410
411 if self.homogeneous:
412 counter = 0
413 for key, val in self.homogeneous.items():
414 s += f'{key:<10} = {val:18.11e}'
415 if key == "V(tot)/v":
416 s += "\n"
417 elif key.lower() == key: # key is all lowercase
418 s += " [per monomer volume]\n"
419 else:
420 s += " [total]\n"
421 counter += 1
422 s += '\n'
423
424 if self.homogeneousSpecies:
425 s += 'Species:\n'
426 s += ' i mu phi(homo) deltaV\n'
427 for sp in self.homogeneousSpecies:
428 s += f'{sp["i"]:>5}'
429 s += f'{sp["mu"]:>20.11e}'
430 s += f'{sp["phi(homo)"]:>20.11e}'
431 s += f'{sp["deltaV"]:>20.11e}\n'
432 s += '\n'
433
434 if self.stress != None:
435 s += 'stress:\n'
436 for i in range(len(self.stress)):
437 v = f'{self.stress[i]:.11e}'
438 s += f'{i:>5}{v:>20}\n'
439 s += '\n'
440
441 if self.environmentStress != None:
442 s += 'environment-modified stress:\n'
443 for i in range(len(self.environmentStress)):
444 v = f'{self.environmentStress[i]:.11e}'
445 s += f'{i:>5}{v:>20}\n'
446 s += '\n'
447
448 return s
449
450
457
458
463 def __init__(self, l):
464 if len(l) == 3:
465 self.phi = float(l[1])
466 self.mu = float(l[2])
467 if len(l) == 2:
468 self.phi = float(l[0])
469 self.mu = float(l[1])
470
471
472
473
538class State:
539
540
545 def __init__(self, filename):
546 with open(filename) as f:
547 firstline = f.readline()
548 fl = firstline.split()
549 if fl[0] != 'System{':
550 raise Exception('Not valid State file')
551 else:
552 self.param = param.Composite(f, 'System')
553 self.thermo = Thermo()
554 self.thermo.read(f)
555
556
563 def __str__(self):
564 out = self.param.__str__() + '\n' + self.thermo.__str__()
565 return out
566
567
575 def write(self, filename):
576 with open(filename, "w") as f:
577 f.write(self.__str__())
578
579
580
623class Sweep:
624
625
632 def __init__(self, prefix):
633 self.sweep = []
634 num = 0
635 filename = prefix + str(num) + '.stt'
636 while os.path.isfile(filename) == True:
637 s = State(filename)
638 self.sweep.append(s)
639 num += 1
640 filename = prefix + str(num) + '.stt'
641
642
713 def summary(self, vars, index = False):
714 n = len(vars)
715
716 summary = []
717
718 for i in range(0, len(self.sweep)):
719 if index == True:
720 s = [i]
721 else:
722 s = []
723 for j in range(0, n):
724 a = self.sweep[i]
725 string = 'a.' + vars[j]
726 try:
727 val = eval(string)
728 except RecursionError:
729 raise Exception('Wrong command or values do not exist')
730 s.append(val)
731 summary.append(s)
732
733 return summary
734
735
736
767 def summaryString(self, vars):
768 n = len(vars)
769
770 summary = []
771
772 for i in range(0, len(self.sweep)):
773 s = [i]
774 for j in range(0, n):
775 a = self.sweep[i]
776 string = 'a.' + vars[j]
777 try:
778 val = eval(string)
779 except RecursionError:
780 raise Exception('Wrong command or values do not exist')
781 s.append(val)
782 summary.append(s)
783
784 nl = [4]
785 nameList = ['step']
786 for i in range(0, n):
787 index = vars[i].rfind('.')
788 name = vars[i][index+1:]
789 nameList.append(name)
790 nl.append(len(name))
791
792 valType = []
793 for i in range(0, len(summary)):
794 valType.append([])
795 for j in range(0, len(summary[0])):
796 valType[i].append(type(summary[i][j]))
797
798 for i in range(0, len(summary)):
799 for j in range(0, len(summary[0])):
800 length = len(str(summary[i][j]))
801 if (valType[i][j] == str) and (length > nl[j]):
802 nl[j] = length
803 if (valType[i][j] == float) and (13 > nl[j]):
804 nl[j] = 13
805 if (valType[i][j] == int) and (length > nl[j]):
806 nl[j] = length
807
808
809 summaryString = ' '
810 for i in range(0, len(nameList)):
811 stringFormat = '{:>' + str(nl[i]) + 's}'
812 if i != len(nameList)-1:
813 summaryString += stringFormat.format(nameList[i]) + ' '
814 else:
815 summaryString += stringFormat.format(nameList[i]) + '\n '
816
817 for i in range(0, len(summary)):
818 for j in range(0, len(summary[0])):
819 if valType[i][j] == int:
820 stringFormat = '{:>' + str(nl[j]) + '}'
821 summaryString += stringFormat.format(summary[i][j])
822 if valType[i][j] == float:
823 stringFormat = '{:.7e}'
824 val = stringFormat.format(summary[i][j])
825 summaryString += val
826 if valType[i][j] == str:
827 stringFormat = '{:>' + str(nl[j]) + 's}'
828 summaryString += stringFormat.format(summary[i][j])
829 if j == len(summary[0])-1:
830 summaryString += '\n '
831 else:
832 summaryString += ' '
833
834 return summaryString
835
836
841 def __getitem__(self, key):
842 return self.sweep[key]
843
844
847 def __len__(self):
848 return len(self.sweep)
849
Container for phi and mu for a single species in a Thermo object.
Definition output.py:456
__init__(self, l)
Constructor.
Definition output.py:463
Container for data in state files produced by a sweep.
Definition output.py:538
__str__(self)
Return string representation of this object in state file format.
Definition output.py:563
write(self, filename)
Write the contents of this object to file in state file format.
Definition output.py:575
__init__(self, filename)
Constructor.
Definition output.py:545
Container for data in state files produced by a PSCF sweep.
Definition output.py:623
__init__(self, prefix)
Constructor.
Definition output.py:632
__getitem__(self, key)
Get a specific State object, specified by integer index.
Definition output.py:841
summaryString(self, vars)
Return a summary report as a formatted string suitable for printing.
Definition output.py:767
__len__(self)
Get the number of states in a sweep.
Definition output.py:847
summary(self, vars, index=False)
Make a summary report containing values for selected variables.
Definition output.py:713
Parser and container for PSCF thermo file blocks.
Definition output.py:179
__init__(self, filename=None)
Constructor.
Definition output.py:191
write(self, filename)
Write the contents of this object in thermo file format to a file.
Definition output.py:355
__str__(self)
Return a string representation of this object in thermo file format.
Definition output.py:362
read(self, file)
Read the passed-in open-file.
Definition output.py:218
skipEmptyLine(self, file)
Skip empty lines in the file.
Definition output.py:340
Container for data of a Composite in a param file.
Definition param.py:197
Module for parsing param files.
Definition param.py:1