From d0757d8f727516d6d358777255bfdba7647dff84 Mon Sep 17 00:00:00 2001 From: reeschang Date: Mon, 1 Jul 2019 15:22:48 -0700 Subject: [PATCH 01/11] IO for ShengBTE CONTROL files --- pymatgen/io/shengbte.py | 275 +++++++++++++++++++++++++++++ pymatgen/io/tests/test_shengbte.py | 102 +++++++++++ 2 files changed, 377 insertions(+) create mode 100644 pymatgen/io/shengbte.py create mode 100644 pymatgen/io/tests/test_shengbte.py diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py new file mode 100644 index 00000000000..74b4ee105bc --- /dev/null +++ b/pymatgen/io/shengbte.py @@ -0,0 +1,275 @@ +# pymatgen>pymatgen>io + +# coding: utf-8 +# Copyright (c) Pymatgen Development Team. +# Distributed under the terms of the MIT License + + +import f90nml +import numpy as np +import re + +""" +This module defines IO ShengBTE for reading, updating, and writing the +CONTROL input file +""" + +__author__ = "Rees Chang" +__copyright__ = "Copyright 2019, The Materials Project" +__version__ = "0.1" +__email__ = "rc564@cornell.edu" +__date__ = "June 27, 2019" + + +class ShengBTE_CONTROL_IO: + + """ + Class for reading, updating, and writing ShengBTE CONTROL files. + Currently only supports ShengBTE options relevant to CSLD. + """ + + def read_CONTROL(self, filename): + """ + Read a CONTROL namelist file and output a namelist object + :param filename: Name of the CONTROL file if in current directory. + If not, use full path. + :return: Dictionary of CONTROL parameters. + """ + nml = f90nml.read(filename) + sdict = nml.todict() + if 't' in sdict['parameters']: + sdict['parameters']['T'] = sdict['parameters']['t'] + del sdict['parameters']['t'] + if 't_min' in sdict['parameters']: + sdict['parameters']['T_min'] = sdict['parameters']['t_min'] + del sdict['parameters']['t_min'] + if 't_max' in sdict['parameters']: + sdict['parameters']['T_max'] = sdict['parameters']['t_max'] + del sdict['parameters']['t_max'] + if 't_step' in sdict['parameters']: + sdict['parameters']['T_step'] = sdict['parameters']['t_step'] + del sdict['parameters']['t_step'] + return sdict + + def file_writer_helper_func(self, dict, filename): + """ + Helper function, not meant to be called directly + """ + nelements = str(dict['allocations']['nelements']) + natoms = str(dict['allocations']['natoms']) + ngrid = dict['allocations']['ngrid'] + norientations = str(dict['allocations']['norientations']) + + lfactor = str(dict['crystal']['lfactor']) + lattvec1 = dict['crystal']['lattvec'][0] + lattvec2 = dict['crystal']['lattvec'][1] + lattvec3 = dict['crystal']['lattvec'][2] + elements = dict['crystal']['elements'] + types = dict['crystal']['types'] + positions = np.asarray(dict['crystal']['positions']) + scell = dict['crystal']['scell'] + # new from here + epsilon1 = dict['crystal']['epsilon'][0] + epsilon2 = dict['crystal']['epsilon'][1] + epsilon3 = dict['crystal']['epsilon'][2] + born = np.asarray(dict['crystal']['born']) + orientations = np.asarray(dict['crystal']['orientations']) + + temperature = str(int(dict['parameters']['T'])) + scalebroad = str(dict['parameters']['scalebroad']) + t_min = str(dict['parameters']['T_min']) + t_max = str(dict['parameters']['T_max']) + t_step = str(dict['parameters']['T_step']) + omega_max = str(dict['parameters']['omega_max']) + rmin = str(dict['parameters']['rmin']) + rmax = str(dict['parameters']['rmax']) + dr = str(dict['parameters']['dr']) + maxiter = str(dict['parameters']['maxiter']) + nticks = str(dict['parameters']['nticks']) + eps = str(dict['parameters']['eps']) + + onlyharmonic = dict['flags']['onlyharmonic'] + isotopes = dict['flags']['isotopes'] + nonanalytic = dict['flags']['nonanalytic'] + nanowires = dict['flags']['nanowires'] + convergence = dict['flags']['convergence'] + autoisotopes = dict['flags']['autoisotopes'] + espresso = dict['flags']['espresso'] + + def boolean_to_string(boolean): + if boolean is not None: + if boolean is True: + return '.TRUE.' + else: + return '.FALSE.' + else: + return 'None' + + #Write strings for types, positions, and born + num_sites, _ = positions.shape + indent = ' ' + types_string = 'types=' + positions_string = '' + born_string = '' + for line in range(num_sites): + if line != num_sites-1: + types_string += str(types[line])+' ' + else: + types_string += str(types[line])+',\n' + + positions_string += indent+'positions(:,' + str(line+1) + ')=' + str(positions[line,0]) + ' ' \ + + str(positions[line,1]) + ' ' + str(positions[line,2]) + ',\n' + + for i in range(3): + born_string += indent+'born(:,'+str(i+1)+','+str(line+1)+')='+str(born[line][i][0])+' '\ + +str(born[line][i][1])+' '+str(born[line][i][2])+',\n' + + #Write string for orientations + num_orientations = dict['allocations']['norientations'] + orientations_string = '' + for o in range(num_orientations): + if o != num_orientations-1: + orientations_string += indent+'orientations(:,'+str(o+1)+')='+str(orientations[o][0])+' '+\ + str(orientations[o][1])+' '+str(orientations[o][2])+',\n' + else: + orientations_string += indent + 'orientations(:,' + str(o + 1) + ')=' + str(orientations[o][0]) + ' ' + \ + str(orientations[o][1]) + ' ' + str(orientations[o][2]) + '\n' + + #masses, gfactors + + + full_string = '&allocations\n'+indent+'nelements='+nelements+',\n' + full_string += indent+'natoms='+natoms+',\n' + full_string += indent+'ngrid(:)='+str(ngrid[0])+' '+str(ngrid[1])+' '+str(ngrid[2])+'\n' + full_string += indent+'norientations='+norientations+'\n' + + full_string += '&end\n&crystal\n' + full_string += indent+'lfactor='+lfactor+',\n' + full_string += indent+'lattvec(:,1)='+str(lattvec1[0])+' '+str(lattvec1[1])+' '+str(lattvec1[2])+',\n' + full_string += indent+'lattvec(:,2)='+str(lattvec2[0])+' '+str(lattvec2[1])+' '+str(lattvec2[2])+',\n' + full_string += indent+'lattvec(:,3)='+str(lattvec3[0])+' '+str(lattvec3[1])+' '+str(lattvec3[2])+',\n' + full_string += indent+'elements=' + if isinstance(elements, list): + for i in range(len(elements)): + full_string += '\"'+elements[i]+str('\"') + if i != (len(elements)-1): + full_string += ' ' + else: + full_string += '\n' + else: + full_string += '\"'+elements+str('\"\n') + full_string += indent+types_string + full_string += positions_string + full_string += indent+'epsilon(:,1)='+str(epsilon1[0])+' '+str(epsilon1[1])+' '+str(epsilon1[2])+',\n' + full_string += indent+'epsilon(:,2)='+str(epsilon2[0])+' '+str(epsilon2[1])+' '+str(epsilon2[2])+',\n' + full_string += indent+'epsilon(:,3)='+str(epsilon3[0])+' '+str(epsilon3[1])+' '+str(epsilon3[2])+',\n' + full_string += born_string + full_string += indent+'scell(:)='+str(scell[0])+' '+str(scell[1])+' '+str(scell[2])+'\n' + full_string += orientations_string + + full_string += '&end\n¶meters\n' + full_string += indent+'T='+temperature+'\n' + full_string += indent+'scalebroad='+scalebroad+'\n' + full_string += indent+'T_min='+t_min+'\n' + full_string += indent+'T_max='+t_max+'\n' + full_string += indent+'T_step='+t_step+'\n' + full_string += indent+'omega_max='+omega_max+'\n' + full_string += indent+'rmin='+rmin+'\n' + full_string += indent+'rmax='+rmax+'\n' + full_string += indent+'dr='+dr+'\n' + full_string += indent+'maxiter='+maxiter+'\n' + full_string += indent+'nticks='+nticks+'\n' + full_string += indent+'eps='+eps+'\n' + + full_string += '&end\n&flags\n' + full_string += indent+'isotopes='+boolean_to_string(isotopes)+'\n' + full_string += indent+'onlyharmonic='+boolean_to_string(onlyharmonic)+'\n' + full_string += indent+'nonanalytic='+boolean_to_string(nonanalytic)+'\n' + full_string += indent+'nanowires='+boolean_to_string(nanowires)+'\n' + full_string += indent + 'convergence=' + boolean_to_string(convergence) + '\n' + full_string += indent + 'autoisotopes=' + boolean_to_string(autoisotopes) + '\n' + full_string += indent + 'espresso=' + boolean_to_string(espresso) + '\n' + full_string += '&end' + + def remove_substring(substring, string): + #Removes lines from 'string' containing 'substring' + return re.sub('.*'+substring+'.*\n?', '', string) + + full_string = remove_substring('None', full_string) + file = open(filename, 'w+') + file.write(full_string) + file.close() + + def write_CONTROL_from_dict(self, dict, filename='CONTROL'): + """ + Write a CONTROL file from a Python dictionary. + Description and default parameters can be found at + https://bitbucket.org/sousaw/shengbte/src/master/. + Note some parameters are mandatory. Optional parameters + default here to None and will not be written to file. + + Args: + dict: A Python dictionary of ShengBTE input parameters. + filename: Filename to save the CONTROL file + """ + new_dict = {'allocations': + {'nelements': dict.get('allocations', None).get('nelements', None), + 'natoms': dict.get('allocations', None).get('natoms', None), + 'ngrid': dict.get('allocations', None).get('ngrid', None), + 'norientations': dict.get('allocations', 0).get('norientations', 0)}, + 'crystal': + {'lfactor': dict.get('crystal', 1.0).get('lfactor', 1.0), + 'lattvec': dict.get('crystal', 'mandatory').get('lattvec', 'mandatory'), + 'types': dict.get('crystal', 'mandatory').get('types', 'mandatory'), + 'elements': dict.get('crystal', 'mandatory').get('elements', 'mandatory'), + 'positions': dict.get('crystal', 'mandatory').get('positions', 'mandatory'), + 'masses': dict.get('crystal', None).get('masses', None), #new + 'gfactors': dict.get('crystal', None).get('gfactors', None), #new + 'epsilon': dict.get('crystal', None).get('epsilon', None), + 'born': dict.get('crystal', None).get('born', None), + 'scell': dict.get('crystal', 'mandatory').get('scell', 'mandatory'), + 'orientations': dict.get('crystal', None).get('orientations', None)}, + 'parameters': + {'T': dict.get('parameters', 'mandatory').get('T', 'mandatory'), + 'T_min': dict.get('parameters', None).get('T_min', None), #new + 'T_max': dict.get('parameters', None).get('T_max', None), #new + 'T_step': dict.get('parameters', None).get('T_step', None), #new + 'omega_max': dict.get('parameters', None).get('omega_max', None), #new + 'scalebroad': dict.get('parameters', 1.0).get('scalebroad', 1.0), + 'rmin': dict.get('parameters', None).get('rmin', None), #new + 'rmax': dict.get('parameters', None).get('rmax', None), #new + 'dr': dict.get('parameters', None).get('dr', None), #new + 'maxiter': dict.get('parameters', None).get('maxiter', None), #new + 'nticks': dict.get('parameters', None).get('nticks', None), #new + 'eps': dict.get('parameters', None).get('eps', None)}, #new + 'flags': + {'nonanalytic': dict.get('flags', None).get('nonanalytic', None), + 'convergence': dict.get('flags', None).get('convergence', None), + 'isotopes': dict.get('flags', None).get('isotopes', None), + 'autoisotopes': dict.get('flags', None).get('autoisotopes', None), + 'nanowires': dict.get('flags', None).get('nanowires', None), + 'onlyharmonic': dict.get('flags', None).get('onlyharmonic', None), + 'espresso': dict.get('flags', None).get('espresso', None)} + } + self.file_writer_helper_func(new_dict, filename=filename) + + +def main(): + io = ShengBTE_CONTROL_IO() + sdict = io.read_CONTROL('CONTROL') + print(sdict) + print(isinstance(sdict, dict)) + print(sdict['crystal']['born']) + print(sdict['crystal']['born'][1][0][0]) + + print(isinstance(sdict['crystal']['types'],list)) + print(sdict['parameters']['T']) + print(sdict['crystal']['types']) + print(type(sdict['crystal']['types'][0])) + print(type(sdict['crystal']['types'][1])) + + io.write_CONTROL_from_dict(sdict, filename='CONTROL_test1') + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/pymatgen/io/tests/test_shengbte.py b/pymatgen/io/tests/test_shengbte.py new file mode 100644 index 00000000000..ff7be386521 --- /dev/null +++ b/pymatgen/io/tests/test_shengbte.py @@ -0,0 +1,102 @@ +# coding: utf-8 +# Copyright (c) Pymatgen Development Team. +# Distributed under the terms of the MIT License. + +import os +import unittest + +from pymatgen.io.shengbte import ShengBTE_CONTROL_IO +from pymatgen.util.testing import PymatgenTest + + + +test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "..", "..", "..", "test_files", "shengbte") + +this_dir = os.path.dirname(os.path.abspath(__file__)) + + +class TestShengBTE(PymatgenTest): + + def setUp(self): + self.sbte_io = ShengBTE_CONTROL_IO() + self.test_dict = { + 'allocations': + {'nelements': 1, + 'natoms': 2, + 'ngrid': [25, 25, 25], + 'norientations': 0}, + 'crystal': + {'lfactor': 0.1, + 'lattvec': [[0.0, 2.734363999, 2.734363999], + [2.734363999, 0.0, 2.734363999], + [2.734363999, 2.734363999, 0.0]], + 'elements': 'Si', + 'types': [1, 1], + 'positions': [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]], + 'scell': [5, 5, 5]}, + 'parameters': + {'T': 500, + 'scalebroad': 0.5}, + 'flags': + {'isotopes': False, + 'onlyharmonic': False, + 'nonanalytic': False, + 'nanowires': False} + } + + def test_read_CONTROL(self): + filename = os.path.join(test_dir, "CONTROL-CSLD_Si") + sbte_dict = self.sbte_io.read_CONTROL(filename) + + self.assertIsInstance(sbte_dict, dict) + self.assertEqual(sbte_dict['allocations']['nelements'], 1) + self.assertEqual(sbte_dict['allocations']['natoms'], 2) + self.assertArrayEqual(sbte_dict['allocations']['ngrid'], [25, 25, 25]) + self.assertEqual(sbte_dict['allocations']['norientations'], 0) + self.assertEqual(sbte_dict['crystal']['lfactor'], 0.1) + self.assertEqual(sbte_dict['crystal']['lattvec'][0], [0.0, 2.734363999, 2.734363999]) + self.assertEqual(sbte_dict['crystal']['lattvec'][1], [2.734363999, 0.0, 2.734363999]) + self.assertEqual(sbte_dict['crystal']['lattvec'][2], [2.734363999, 2.734363999, 0.0]) + self.assertIsInstance(sbte_dict['crystal']['elements'], (list, str)) + if isinstance(sbte_dict['crystal']['elements'], list): + all_strings = all(isinstance(item, str) for item in sbte_dict['crystal']['elements']) + self.assertTrue(all_strings) + self.assertIsInstance(sbte_dict['crystal']['types'], (list, int)) + if isinstance(sbte_dict['crystal']['types'], list): + all_ints = all(isinstance(item, int) for item in sbte_dict['crystal']['types']) + self.assertTrue(all_ints) + self.assertArrayEqual(sbte_dict['crystal']['positions'], [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]) + self.assertArrayEqual(sbte_dict['crystal']['scell'], [5, 5, 5]) + self.assertEqual(sbte_dict['parameters']['T'], 500) + self.assertEqual(sbte_dict['parameters']['scalebroad'], 0.5) + self.assertFalse(sbte_dict['flags']['isotopes']) + self.assertFalse(sbte_dict['flags']['onlyharmonic']) + self.assertFalse(sbte_dict['flags']['nonanalytic']) + self.assertFalse(sbte_dict['flags']['nanowires']) + + def test_file_writer_helper_func(self): + + if os.path.exists(os.path.join(test_dir,'test_control')): + os.remove(os.path.join(test_dir,'test_control')) + self.sbte_io.file_writer_helper_func(self.test_dict, filename=os.path.join(test_dir,'test_control')) + + with open(os.path.join(test_dir,'test_control'), 'r') as file: + test_string = file.read() + with open(os.path.join(test_dir, "CONTROL-CSLD_Si"), 'r') as reference_file: + reference_string = reference_file.read() + self.assertMultiLineEqual(test_string, reference_string) + os.remove(os.path.join(test_dir, 'test_control')) + + def test_write_CONTROL_from_dict(self): + if os.path.exists(os.path.join(test_dir,'test_control')): + os.remove(os.path.join(test_dir,'test_control')) + self.sbte_io.write_CONTROL_from_dict(self.test_dict, filename=os.path.join(test_dir,'test_control')) + with open(os.path.join(test_dir,'test_control'), 'r') as file: + test_string = file.read() + with open(os.path.join(test_dir, "CONTROL-CSLD_Si"), 'r') as reference_file: + reference_string = reference_file.read() + self.assertMultiLineEqual(test_string, reference_string) + os.remove(os.path.join(test_dir, 'test_control')) + + From a57924cf8c6b02d6120135402e770fc6189a37f4 Mon Sep 17 00:00:00 2001 From: reeschang Date: Mon, 1 Jul 2019 15:45:23 -0700 Subject: [PATCH 02/11] added file for shengbte unit test --- test_files/shengbte/CONTROL-CSLD_Si | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test_files/shengbte/CONTROL-CSLD_Si diff --git a/test_files/shengbte/CONTROL-CSLD_Si b/test_files/shengbte/CONTROL-CSLD_Si new file mode 100644 index 00000000000..8d5f46d81d1 --- /dev/null +++ b/test_files/shengbte/CONTROL-CSLD_Si @@ -0,0 +1,27 @@ +&allocations + nelements=1, + natoms=2, + ngrid(:)=25 25 25 + norientations=0 +&end +&crystal + lfactor=0.1, + lattvec(:,1)=0.0 2.734363999 2.734363999, + lattvec(:,2)=2.734363999 0.0 2.734363999, + lattvec(:,3)=2.734363999 2.734363999 0.0, + elements="Si" + types=1 1, + positions(:,1)=0.0 0.0 0.0, + positions(:,2)=0.25 0.25 0.25, + scell(:)=5 5 5 +&end +¶meters + T=500 + scalebroad=0.5 +&end +&flags + isotopes=.FALSE. + onlyharmonic=.FALSE. + nonanalytic=.FALSE. + nanowires=.FALSE. +&end \ No newline at end of file From e7cadf6e075580994c3ba555e85e3a121c548004 Mon Sep 17 00:00:00 2001 From: reeschang Date: Fri, 5 Jul 2019 13:32:30 -0700 Subject: [PATCH 03/11] Revised IO functions and test cases --- pymatgen/io/shengbte.py | 331 ++++++++++++++++++----------- pymatgen/io/tests/test_shengbte.py | 68 +++--- 2 files changed, 237 insertions(+), 162 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index 74b4ee105bc..51beabecfb1 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -1,5 +1,3 @@ -# pymatgen>pymatgen>io - # coding: utf-8 # Copyright (c) Pymatgen Development Team. # Distributed under the terms of the MIT License @@ -21,21 +19,124 @@ __date__ = "June 27, 2019" -class ShengBTE_CONTROL_IO: +class Control: """ Class for reading, updating, and writing ShengBTE CONTROL files. Currently only supports ShengBTE options relevant to CSLD. """ - def read_CONTROL(self, filename): + def __init__(self, + alloc_dict={}, + crystal_dict={}, + params_dict={}, + flags_dict={}): + """ + Args: + alloc_dict (dict): ShengBTE 'allocations' parameters + crystal_dict (dict): ShengBTE 'crystal' parameters + params_dict (dict): ShengBTE 'parameters' parameters + flags_dict (dict): ShengBTE 'flags' parameters + """ + + self.alloc_dict = { + 'nelements': None, + 'natoms': None, + 'ngrid': None, + 'norientations': 0, + } + self.alloc_dict.update(alloc_dict) + + self.crystal_dict = { + 'lfactor': 0.1, + 'lattvec': None, #required + 'types': None, #required + 'elements': None, #required + 'positions': None, #required + 'masses': None, + 'gfactors': None, + 'epsilon': None, + 'born': None, + 'scell': None, #required + 'orientations': None + } + self.crystal_dict.update(crystal_dict) + + self.params_dict = { + 'T': 300, #required + 'T_min': None, + 'T_max': None, + 'T_step': None, + 'omega_max': None, + 'scalebroad': 0.5, #required + 'rmin': None, + 'rmax': None, + 'dr': None, + 'maxiter': None, + 'nticks': None, + 'eps': None + } + self.params_dict.update(params_dict) + + self.flags_dict = { + 'nonanalytic': None, + 'convergence': None, + 'isotopes': None, + 'autoisotopes': None, + 'nanowires': None, + 'onlyharmonic': None, + 'espresso': None + } + self.flags_dict.update(flags_dict) + + def check_required_params(): + """ + Raise error if any required parameters are missing + """ + required_params = {'crystal': + ['lattvec', + 'types', + 'elements', + 'positions', + 'scell'], + 'parameters': + ['T', + 'scalebroad'], + } + required_namelists = list(required_params.keys()) + for required_namelist in required_namelists: + required_namelist_params = required_params[required_namelist] + for required_namelist_param in required_namelist_params: + if required_namelist == 'allocations' and \ + self.alloc_dict[required_namelist_param] is None: + raise AttributeError('Missing argument: {}>{}'.format(required_namelist, + required_namelist_param)) + elif required_namelist == 'crystal' and \ + self.crystal_dict[required_namelist_param] is None: + raise AttributeError('Missing argument: {}>{}'.format(required_namelist, + required_namelist_param)) + elif required_namelist == 'parameters' and \ + self.params_dict[required_namelist_param] is None: + raise AttributeError('Missing argument: {}>{}'.format(required_namelist, + required_namelist_param)) + elif required_namelist == 'flags' and \ + self.flags_dict[required_namelist_param] is None: + raise AttributeError('Missing argument: {}>{}'.format(required_namelist, + required_namelist_param)) + check_required_params() + + @classmethod + def from_file(cls, filepath): """ - Read a CONTROL namelist file and output a namelist object - :param filename: Name of the CONTROL file if in current directory. - If not, use full path. - :return: Dictionary of CONTROL parameters. + Read a CONTROL namelist file and output a 'Control' object + + Args: + filepath (String): Path of the CONTROL file. + + Returns: + 'Control' object with parameters instantiated. """ - nml = f90nml.read(filename) + nml = f90nml.read(filepath) sdict = nml.todict() if 't' in sdict['parameters']: sdict['parameters']['T'] = sdict['parameters']['t'] @@ -49,52 +150,101 @@ def read_CONTROL(self, filename): if 't_step' in sdict['parameters']: sdict['parameters']['T_step'] = sdict['parameters']['t_step'] del sdict['parameters']['t_step'] - return sdict - def file_writer_helper_func(self, dict, filename): + alloc_dict = sdict['allocations'] + crystal_dict = sdict['crystal'] + params_dict = sdict['parameters'] + flags_dict = sdict['flags'] + + return cls(alloc_dict, crystal_dict, params_dict, flags_dict) + + @classmethod + def from_dict(cls, sdict): """ - Helper function, not meant to be called directly + Write a CONTROL file from a Python dictionary. + Description and default parameters can be found at + https://bitbucket.org/sousaw/shengbte/src/master/. + Note some parameters are mandatory. Optional parameters + default here to None and will not be written to file. + + Args: + dict: A Python dictionary of ShengBTE input parameters. + filename: Filename to save the CONTROL file """ - nelements = str(dict['allocations']['nelements']) - natoms = str(dict['allocations']['natoms']) - ngrid = dict['allocations']['ngrid'] - norientations = str(dict['allocations']['norientations']) - - lfactor = str(dict['crystal']['lfactor']) - lattvec1 = dict['crystal']['lattvec'][0] - lattvec2 = dict['crystal']['lattvec'][1] - lattvec3 = dict['crystal']['lattvec'][2] - elements = dict['crystal']['elements'] - types = dict['crystal']['types'] - positions = np.asarray(dict['crystal']['positions']) - scell = dict['crystal']['scell'] + + try: + alloc_dict = sdict['allocations'] + except: + alloc_dict = {} + try: + crystal_dict = sdict['crystal'] + except: + crystal_dict = {} + try: + params_dict = sdict['parameters'] + except: + params_dict = {} + try: + flags_dict = sdict['flags'] + except: + flags_dict = {} + + return cls(alloc_dict, crystal_dict, params_dict, flags_dict) + + def to_file(self, filename): + """ + Writes ShengBTE CONTROL file from 'Control' object + """ + positions = np.asarray(self.crystal_dict['positions']) + num_sites, _ = positions.shape + + nelements = str(self.alloc_dict['nelements']) + natoms = str(self.alloc_dict['natoms']) + ngrid = self.alloc_dict['ngrid'] + norientations = str(self.alloc_dict['norientations']) + + lfactor = str(self.crystal_dict['lfactor']) + lattvec1 = self.crystal_dict['lattvec'][0] + lattvec2 = self.crystal_dict['lattvec'][1] + lattvec3 = self.crystal_dict['lattvec'][2] + elements = self.crystal_dict['elements'] + types = self.crystal_dict['types'] + scell = self.crystal_dict['scell'] # new from here - epsilon1 = dict['crystal']['epsilon'][0] - epsilon2 = dict['crystal']['epsilon'][1] - epsilon3 = dict['crystal']['epsilon'][2] - born = np.asarray(dict['crystal']['born']) - orientations = np.asarray(dict['crystal']['orientations']) - - temperature = str(int(dict['parameters']['T'])) - scalebroad = str(dict['parameters']['scalebroad']) - t_min = str(dict['parameters']['T_min']) - t_max = str(dict['parameters']['T_max']) - t_step = str(dict['parameters']['T_step']) - omega_max = str(dict['parameters']['omega_max']) - rmin = str(dict['parameters']['rmin']) - rmax = str(dict['parameters']['rmax']) - dr = str(dict['parameters']['dr']) - maxiter = str(dict['parameters']['maxiter']) - nticks = str(dict['parameters']['nticks']) - eps = str(dict['parameters']['eps']) - - onlyharmonic = dict['flags']['onlyharmonic'] - isotopes = dict['flags']['isotopes'] - nonanalytic = dict['flags']['nonanalytic'] - nanowires = dict['flags']['nanowires'] - convergence = dict['flags']['convergence'] - autoisotopes = dict['flags']['autoisotopes'] - espresso = dict['flags']['espresso'] + if self.crystal_dict['epsilon'] is not None: + epsilon1 = self.crystal_dict['epsilon'][0] + epsilon2 = self.crystal_dict['epsilon'][1] + epsilon3 = self.crystal_dict['epsilon'][2] + else: + epsilon1 = np.full(3, None) + epsilon2 = np.full(3, None) + epsilon3 = np.full(3, None) + if self.crystal_dict['born'] is not None: + born = np.asarray(self.crystal_dict['born']) + else: + born = np.full((num_sites, 3, 3), None) + orientations = np.asarray(self.crystal_dict['orientations']) + + temperature = str(int(self.params_dict['T'])) + scalebroad = str(self.params_dict['scalebroad']) + t_min = str(self.params_dict['T_min']) + t_max = str(self.params_dict['T_max']) + t_step = str(self.params_dict['T_step']) + omega_max = str(self.params_dict['omega_max']) + rmin = str(self.params_dict['rmin']) + rmax = str(self.params_dict['rmax']) + dr = str(self.params_dict['dr']) + maxiter = str(self.params_dict['maxiter']) + nticks = str(self.params_dict['nticks']) + eps = str(self.params_dict['eps']) + + onlyharmonic = self.flags_dict['onlyharmonic'] + isotopes = self.flags_dict['isotopes'] + nonanalytic = self.flags_dict['nonanalytic'] + nanowires = self.flags_dict['nanowires'] + convergence = self.flags_dict['convergence'] + autoisotopes = self.flags_dict['autoisotopes'] + espresso = self.flags_dict['espresso'] def boolean_to_string(boolean): if boolean is not None: @@ -106,7 +256,6 @@ def boolean_to_string(boolean): return 'None' #Write strings for types, positions, and born - num_sites, _ = positions.shape indent = ' ' types_string = 'types=' positions_string = '' @@ -125,7 +274,7 @@ def boolean_to_string(boolean): +str(born[line][i][1])+' '+str(born[line][i][2])+',\n' #Write string for orientations - num_orientations = dict['allocations']['norientations'] + num_orientations = self.alloc_dict['norientations'] orientations_string = '' for o in range(num_orientations): if o != num_orientations-1: @@ -198,78 +347,4 @@ def remove_substring(substring, string): full_string = remove_substring('None', full_string) file = open(filename, 'w+') file.write(full_string) - file.close() - - def write_CONTROL_from_dict(self, dict, filename='CONTROL'): - """ - Write a CONTROL file from a Python dictionary. - Description and default parameters can be found at - https://bitbucket.org/sousaw/shengbte/src/master/. - Note some parameters are mandatory. Optional parameters - default here to None and will not be written to file. - - Args: - dict: A Python dictionary of ShengBTE input parameters. - filename: Filename to save the CONTROL file - """ - new_dict = {'allocations': - {'nelements': dict.get('allocations', None).get('nelements', None), - 'natoms': dict.get('allocations', None).get('natoms', None), - 'ngrid': dict.get('allocations', None).get('ngrid', None), - 'norientations': dict.get('allocations', 0).get('norientations', 0)}, - 'crystal': - {'lfactor': dict.get('crystal', 1.0).get('lfactor', 1.0), - 'lattvec': dict.get('crystal', 'mandatory').get('lattvec', 'mandatory'), - 'types': dict.get('crystal', 'mandatory').get('types', 'mandatory'), - 'elements': dict.get('crystal', 'mandatory').get('elements', 'mandatory'), - 'positions': dict.get('crystal', 'mandatory').get('positions', 'mandatory'), - 'masses': dict.get('crystal', None).get('masses', None), #new - 'gfactors': dict.get('crystal', None).get('gfactors', None), #new - 'epsilon': dict.get('crystal', None).get('epsilon', None), - 'born': dict.get('crystal', None).get('born', None), - 'scell': dict.get('crystal', 'mandatory').get('scell', 'mandatory'), - 'orientations': dict.get('crystal', None).get('orientations', None)}, - 'parameters': - {'T': dict.get('parameters', 'mandatory').get('T', 'mandatory'), - 'T_min': dict.get('parameters', None).get('T_min', None), #new - 'T_max': dict.get('parameters', None).get('T_max', None), #new - 'T_step': dict.get('parameters', None).get('T_step', None), #new - 'omega_max': dict.get('parameters', None).get('omega_max', None), #new - 'scalebroad': dict.get('parameters', 1.0).get('scalebroad', 1.0), - 'rmin': dict.get('parameters', None).get('rmin', None), #new - 'rmax': dict.get('parameters', None).get('rmax', None), #new - 'dr': dict.get('parameters', None).get('dr', None), #new - 'maxiter': dict.get('parameters', None).get('maxiter', None), #new - 'nticks': dict.get('parameters', None).get('nticks', None), #new - 'eps': dict.get('parameters', None).get('eps', None)}, #new - 'flags': - {'nonanalytic': dict.get('flags', None).get('nonanalytic', None), - 'convergence': dict.get('flags', None).get('convergence', None), - 'isotopes': dict.get('flags', None).get('isotopes', None), - 'autoisotopes': dict.get('flags', None).get('autoisotopes', None), - 'nanowires': dict.get('flags', None).get('nanowires', None), - 'onlyharmonic': dict.get('flags', None).get('onlyharmonic', None), - 'espresso': dict.get('flags', None).get('espresso', None)} - } - self.file_writer_helper_func(new_dict, filename=filename) - - -def main(): - io = ShengBTE_CONTROL_IO() - sdict = io.read_CONTROL('CONTROL') - print(sdict) - print(isinstance(sdict, dict)) - print(sdict['crystal']['born']) - print(sdict['crystal']['born'][1][0][0]) - - print(isinstance(sdict['crystal']['types'],list)) - print(sdict['parameters']['T']) - print(sdict['crystal']['types']) - print(type(sdict['crystal']['types'][0])) - print(type(sdict['crystal']['types'][1])) - - io.write_CONTROL_from_dict(sdict, filename='CONTROL_test1') - - -if __name__ == '__main__': - main() \ No newline at end of file + file.close() \ No newline at end of file diff --git a/pymatgen/io/tests/test_shengbte.py b/pymatgen/io/tests/test_shengbte.py index ff7be386521..b6c8cd5e0c8 100644 --- a/pymatgen/io/tests/test_shengbte.py +++ b/pymatgen/io/tests/test_shengbte.py @@ -5,7 +5,7 @@ import os import unittest -from pymatgen.io.shengbte import ShengBTE_CONTROL_IO +from pymatgen.io.shengbte import Control from pymatgen.util.testing import PymatgenTest @@ -19,7 +19,7 @@ class TestShengBTE(PymatgenTest): def setUp(self): - self.sbte_io = ShengBTE_CONTROL_IO() + self.filename = os.path.join(test_dir, "CONTROL-CSLD_Si") self.test_dict = { 'allocations': {'nelements': 1, @@ -45,41 +45,40 @@ def setUp(self): 'nanowires': False} } - def test_read_CONTROL(self): - filename = os.path.join(test_dir, "CONTROL-CSLD_Si") - sbte_dict = self.sbte_io.read_CONTROL(filename) - - self.assertIsInstance(sbte_dict, dict) - self.assertEqual(sbte_dict['allocations']['nelements'], 1) - self.assertEqual(sbte_dict['allocations']['natoms'], 2) - self.assertArrayEqual(sbte_dict['allocations']['ngrid'], [25, 25, 25]) - self.assertEqual(sbte_dict['allocations']['norientations'], 0) - self.assertEqual(sbte_dict['crystal']['lfactor'], 0.1) - self.assertEqual(sbte_dict['crystal']['lattvec'][0], [0.0, 2.734363999, 2.734363999]) - self.assertEqual(sbte_dict['crystal']['lattvec'][1], [2.734363999, 0.0, 2.734363999]) - self.assertEqual(sbte_dict['crystal']['lattvec'][2], [2.734363999, 2.734363999, 0.0]) - self.assertIsInstance(sbte_dict['crystal']['elements'], (list, str)) - if isinstance(sbte_dict['crystal']['elements'], list): - all_strings = all(isinstance(item, str) for item in sbte_dict['crystal']['elements']) + def test_from_file(self): + io = Control.from_file(self.filename) + self.assertIsInstance(io.alloc_dict, dict) + self.assertIsInstance(io.crystal_dict, dict) + self.assertIsInstance(io.params_dict, dict) + self.assertIsInstance(io.flags_dict, dict) + self.assertEqual(io.alloc_dict['nelements'], 1) + self.assertEqual(io.alloc_dict['natoms'], 2) + self.assertArrayEqual(io.alloc_dict['ngrid'], [25, 25, 25]) + self.assertEqual(io.alloc_dict['norientations'], 0) + self.assertEqual(io.crystal_dict['lfactor'], 0.1) + self.assertEqual(io.crystal_dict['lattvec'][0], [0.0, 2.734363999, 2.734363999]) + self.assertEqual(io.crystal_dict['lattvec'][1], [2.734363999, 0.0, 2.734363999]) + self.assertEqual(io.crystal_dict['lattvec'][2], [2.734363999, 2.734363999, 0.0]) + self.assertIsInstance(io.crystal_dict['elements'], (list, str)) + if isinstance(io.crystal_dict['elements'], list): + all_strings = all(isinstance(item, str) for item in io.crystal_dict['elements']) self.assertTrue(all_strings) - self.assertIsInstance(sbte_dict['crystal']['types'], (list, int)) - if isinstance(sbte_dict['crystal']['types'], list): - all_ints = all(isinstance(item, int) for item in sbte_dict['crystal']['types']) + self.assertIsInstance(io.crystal_dict['types'], (list, int)) + if isinstance(io.crystal_dict['types'], list): + all_ints = all(isinstance(item, int) for item in io.crystal_dict['types']) self.assertTrue(all_ints) - self.assertArrayEqual(sbte_dict['crystal']['positions'], [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]) - self.assertArrayEqual(sbte_dict['crystal']['scell'], [5, 5, 5]) - self.assertEqual(sbte_dict['parameters']['T'], 500) - self.assertEqual(sbte_dict['parameters']['scalebroad'], 0.5) - self.assertFalse(sbte_dict['flags']['isotopes']) - self.assertFalse(sbte_dict['flags']['onlyharmonic']) - self.assertFalse(sbte_dict['flags']['nonanalytic']) - self.assertFalse(sbte_dict['flags']['nanowires']) - - def test_file_writer_helper_func(self): + self.assertArrayEqual(io.crystal_dict['positions'], [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]) + self.assertArrayEqual(io.crystal_dict['scell'], [5, 5, 5]) + self.assertEqual(io.params_dict['T'], 500) + self.assertEqual(io.params_dict['scalebroad'], 0.5) + self.assertFalse(io.flags_dict['isotopes']) + self.assertFalse(io.flags_dict['onlyharmonic']) + self.assertFalse(io.flags_dict['nonanalytic']) + self.assertFalse(io.flags_dict['nanowires']) if os.path.exists(os.path.join(test_dir,'test_control')): os.remove(os.path.join(test_dir,'test_control')) - self.sbte_io.file_writer_helper_func(self.test_dict, filename=os.path.join(test_dir,'test_control')) + io.to_file(filename=os.path.join(test_dir,'test_control')) with open(os.path.join(test_dir,'test_control'), 'r') as file: test_string = file.read() @@ -88,10 +87,11 @@ def test_file_writer_helper_func(self): self.assertMultiLineEqual(test_string, reference_string) os.remove(os.path.join(test_dir, 'test_control')) - def test_write_CONTROL_from_dict(self): + def test_from_dict(self): + io = Control.from_dict(self.test_dict) if os.path.exists(os.path.join(test_dir,'test_control')): os.remove(os.path.join(test_dir,'test_control')) - self.sbte_io.write_CONTROL_from_dict(self.test_dict, filename=os.path.join(test_dir,'test_control')) + io.to_file(filename=os.path.join(test_dir,'test_control')) with open(os.path.join(test_dir,'test_control'), 'r') as file: test_string = file.read() with open(os.path.join(test_dir, "CONTROL-CSLD_Si"), 'r') as reference_file: From ed019ffba9b3bcd593d3ee1ff439850d172eea54 Mon Sep 17 00:00:00 2001 From: reeschang Date: Mon, 8 Jul 2019 10:58:43 -0700 Subject: [PATCH 04/11] Fixed mutable dict defaults in shengbte.py --- pymatgen/io/shengbte.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index 51beabecfb1..7c2c12a4114 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -27,10 +27,10 @@ class Control: """ def __init__(self, - alloc_dict={}, - crystal_dict={}, - params_dict={}, - flags_dict={}): + alloc_dict=None, + crystal_dict=None, + params_dict=None, + flags_dict=None): """ Args: alloc_dict (dict): ShengBTE 'allocations' parameters @@ -45,7 +45,8 @@ def __init__(self, 'ngrid': None, 'norientations': 0, } - self.alloc_dict.update(alloc_dict) + if alloc_dict: + self.alloc_dict.update(alloc_dict) self.crystal_dict = { 'lfactor': 0.1, @@ -60,7 +61,8 @@ def __init__(self, 'scell': None, #required 'orientations': None } - self.crystal_dict.update(crystal_dict) + if crystal_dict: + self.crystal_dict.update(crystal_dict) self.params_dict = { 'T': 300, #required @@ -76,7 +78,8 @@ def __init__(self, 'nticks': None, 'eps': None } - self.params_dict.update(params_dict) + if params_dict: + self.params_dict.update(params_dict) self.flags_dict = { 'nonanalytic': None, @@ -87,7 +90,8 @@ def __init__(self, 'onlyharmonic': None, 'espresso': None } - self.flags_dict.update(flags_dict) + if flags_dict: + self.flags_dict.update(flags_dict) def check_required_params(): """ From 35b96d422074ebee80a5587a2d7af63a919c933c Mon Sep 17 00:00:00 2001 From: reeschang Date: Mon, 8 Jul 2019 21:14:43 -0700 Subject: [PATCH 05/11] Rewrote ShengBTE IO class for maintainability and MSONability --- pymatgen/io/shengbte.py | 361 ++++++----------------------- pymatgen/io/tests/test_shengbte.py | 55 ++--- requirements-optional.txt | 1 + 3 files changed, 103 insertions(+), 314 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index 7c2c12a4114..e329be5e670 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -1,11 +1,15 @@ # coding: utf-8 # Copyright (c) Pymatgen Development Team. # Distributed under the terms of the MIT License +import warnings +from monty.dev import requires +from monty.json import MSONable -import f90nml -import numpy as np -import re +try: + import f90nml +except: + f90nml = None """ This module defines IO ShengBTE for reading, updating, and writing the @@ -19,118 +23,51 @@ __date__ = "June 27, 2019" -class Control: +class Control(dict, MSONable): """ Class for reading, updating, and writing ShengBTE CONTROL files. - Currently only supports ShengBTE options relevant to CSLD. """ - def __init__(self, - alloc_dict=None, - crystal_dict=None, - params_dict=None, - flags_dict=None): + required_params = ["lattvec", "types", "elements", "positions", "scell", + "t", "scalebroad"] + allocations_keys = ["nelements", "natoms", "ngrid", "norientations"] + crystal_keys = ["lfactor", "lattvec", "types", "elements", "positions", + "masses", "gfactors", "epsilon", "born", "scell", + "orientations"] + params_keys = ["t", "t_min", "t_max", "t_step", "omega_max", "scalebroad", + "rmin", "rmax", "dr", "maxiter", "nticks", "eps"] + flags_keys = ["nonanalytic", "convergence", "isotopes", "autoisotopes", + "nanowires", "onlyharmonic", "espresso"] + + @requires(f90nml, + "ShengBTE Control object requires f90nml to be installed. " \ + "Please get it at https://pypi.org/project/f90nml.") + def __init__(self, ngrid=None, lfactor=0.1, + scalebroad=0.5, t=500, **kwargs): """ + See https://bitbucket.org/sousaw/shengbte/src/master/ for more + detailed description of CONTROL arguments. + Args: - alloc_dict (dict): ShengBTE 'allocations' parameters - crystal_dict (dict): ShengBTE 'crystal' parameters - params_dict (dict): ShengBTE 'parameters' parameters - flags_dict (dict): ShengBTE 'flags' parameters + ngrid (size 3 list): + lfactor (float): + scell (size 3 list): + scalebroad (float): + t (int): Temperature (Kelvin) + **kwargs: Other ShengBTE parameters. """ + if ngrid is None: + ngrid = [25, 25, 25] - self.alloc_dict = { - 'nelements': None, - 'natoms': None, - 'ngrid': None, - 'norientations': 0, - } - if alloc_dict: - self.alloc_dict.update(alloc_dict) - - self.crystal_dict = { - 'lfactor': 0.1, - 'lattvec': None, #required - 'types': None, #required - 'elements': None, #required - 'positions': None, #required - 'masses': None, - 'gfactors': None, - 'epsilon': None, - 'born': None, - 'scell': None, #required - 'orientations': None - } - if crystal_dict: - self.crystal_dict.update(crystal_dict) - - self.params_dict = { - 'T': 300, #required - 'T_min': None, - 'T_max': None, - 'T_step': None, - 'omega_max': None, - 'scalebroad': 0.5, #required - 'rmin': None, - 'rmax': None, - 'dr': None, - 'maxiter': None, - 'nticks': None, - 'eps': None - } - if params_dict: - self.params_dict.update(params_dict) - - self.flags_dict = { - 'nonanalytic': None, - 'convergence': None, - 'isotopes': None, - 'autoisotopes': None, - 'nanowires': None, - 'onlyharmonic': None, - 'espresso': None - } - if flags_dict: - self.flags_dict.update(flags_dict) - - def check_required_params(): - """ - Raise error if any required parameters are missing - """ - required_params = {'crystal': - ['lattvec', - 'types', - 'elements', - 'positions', - 'scell'], - 'parameters': - ['T', - 'scalebroad'], - } - required_namelists = list(required_params.keys()) - for required_namelist in required_namelists: - required_namelist_params = required_params[required_namelist] - for required_namelist_param in required_namelist_params: - if required_namelist == 'allocations' and \ - self.alloc_dict[required_namelist_param] is None: - raise AttributeError('Missing argument: {}>{}'.format(required_namelist, - required_namelist_param)) - elif required_namelist == 'crystal' and \ - self.crystal_dict[required_namelist_param] is None: - raise AttributeError('Missing argument: {}>{}'.format(required_namelist, - required_namelist_param)) - elif required_namelist == 'parameters' and \ - self.params_dict[required_namelist_param] is None: - raise AttributeError('Missing argument: {}>{}'.format(required_namelist, - required_namelist_param)) - elif required_namelist == 'flags' and \ - self.flags_dict[required_namelist_param] is None: - raise AttributeError('Missing argument: {}>{}'.format(required_namelist, - required_namelist_param)) - check_required_params() + self["ngrid"] = ngrid + self["lfactor"] = lfactor + self["scalebroad"] = scalebroad + self["t"] = t + self.update(kwargs) - @classmethod - def from_file(cls, filepath): + @staticmethod + def from_file(filepath): """ Read a CONTROL namelist file and output a 'Control' object @@ -142,25 +79,14 @@ def from_file(cls, filepath): """ nml = f90nml.read(filepath) sdict = nml.todict() - if 't' in sdict['parameters']: - sdict['parameters']['T'] = sdict['parameters']['t'] - del sdict['parameters']['t'] - if 't_min' in sdict['parameters']: - sdict['parameters']['T_min'] = sdict['parameters']['t_min'] - del sdict['parameters']['t_min'] - if 't_max' in sdict['parameters']: - sdict['parameters']['T_max'] = sdict['parameters']['t_max'] - del sdict['parameters']['t_max'] - if 't_step' in sdict['parameters']: - sdict['parameters']['T_step'] = sdict['parameters']['t_step'] - del sdict['parameters']['t_step'] - alloc_dict = sdict['allocations'] - crystal_dict = sdict['crystal'] - params_dict = sdict['parameters'] - flags_dict = sdict['flags'] + all_dict = {} + all_dict.update(sdict["allocations"]) + all_dict.update(sdict["crystal"]) + all_dict.update(sdict["parameters"]) + all_dict.update(sdict["flags"]) - return cls(alloc_dict, crystal_dict, params_dict, flags_dict) + return Control(**all_dict) @classmethod def from_dict(cls, sdict): @@ -173,182 +99,41 @@ def from_dict(cls, sdict): Args: dict: A Python dictionary of ShengBTE input parameters. - filename: Filename to save the CONTROL file """ - - try: - alloc_dict = sdict['allocations'] - except: - alloc_dict = {} - try: - crystal_dict = sdict['crystal'] - except: - crystal_dict = {} - try: - params_dict = sdict['parameters'] - except: - params_dict = {} - try: - flags_dict = sdict['flags'] - except: - flags_dict = {} - - return cls(alloc_dict, crystal_dict, params_dict, flags_dict) + return cls(**sdict) def to_file(self, filename): """ Writes ShengBTE CONTROL file from 'Control' object """ - positions = np.asarray(self.crystal_dict['positions']) - num_sites, _ = positions.shape - - nelements = str(self.alloc_dict['nelements']) - natoms = str(self.alloc_dict['natoms']) - ngrid = self.alloc_dict['ngrid'] - norientations = str(self.alloc_dict['norientations']) - - lfactor = str(self.crystal_dict['lfactor']) - lattvec1 = self.crystal_dict['lattvec'][0] - lattvec2 = self.crystal_dict['lattvec'][1] - lattvec3 = self.crystal_dict['lattvec'][2] - elements = self.crystal_dict['elements'] - types = self.crystal_dict['types'] - scell = self.crystal_dict['scell'] - # new from here - if self.crystal_dict['epsilon'] is not None: - epsilon1 = self.crystal_dict['epsilon'][0] - epsilon2 = self.crystal_dict['epsilon'][1] - epsilon3 = self.crystal_dict['epsilon'][2] - else: - epsilon1 = np.full(3, None) - epsilon2 = np.full(3, None) - epsilon3 = np.full(3, None) - if self.crystal_dict['born'] is not None: - born = np.asarray(self.crystal_dict['born']) - else: - born = np.full((num_sites, 3, 3), None) - orientations = np.asarray(self.crystal_dict['orientations']) - - temperature = str(int(self.params_dict['T'])) - scalebroad = str(self.params_dict['scalebroad']) - t_min = str(self.params_dict['T_min']) - t_max = str(self.params_dict['T_max']) - t_step = str(self.params_dict['T_step']) - omega_max = str(self.params_dict['omega_max']) - rmin = str(self.params_dict['rmin']) - rmax = str(self.params_dict['rmax']) - dr = str(self.params_dict['dr']) - maxiter = str(self.params_dict['maxiter']) - nticks = str(self.params_dict['nticks']) - eps = str(self.params_dict['eps']) - - onlyharmonic = self.flags_dict['onlyharmonic'] - isotopes = self.flags_dict['isotopes'] - nonanalytic = self.flags_dict['nonanalytic'] - nanowires = self.flags_dict['nanowires'] - convergence = self.flags_dict['convergence'] - autoisotopes = self.flags_dict['autoisotopes'] - espresso = self.flags_dict['espresso'] - - def boolean_to_string(boolean): - if boolean is not None: - if boolean is True: - return '.TRUE.' - else: - return '.FALSE.' - else: - return 'None' - - #Write strings for types, positions, and born - indent = ' ' - types_string = 'types=' - positions_string = '' - born_string = '' - for line in range(num_sites): - if line != num_sites-1: - types_string += str(types[line])+' ' - else: - types_string += str(types[line])+',\n' - - positions_string += indent+'positions(:,' + str(line+1) + ')=' + str(positions[line,0]) + ' ' \ - + str(positions[line,1]) + ' ' + str(positions[line,2]) + ',\n' - - for i in range(3): - born_string += indent+'born(:,'+str(i+1)+','+str(line+1)+')='+str(born[line][i][0])+' '\ - +str(born[line][i][1])+' '+str(born[line][i][2])+',\n' - - #Write string for orientations - num_orientations = self.alloc_dict['norientations'] - orientations_string = '' - for o in range(num_orientations): - if o != num_orientations-1: - orientations_string += indent+'orientations(:,'+str(o+1)+')='+str(orientations[o][0])+' '+\ - str(orientations[o][1])+' '+str(orientations[o][2])+',\n' - else: - orientations_string += indent + 'orientations(:,' + str(o + 1) + ')=' + str(orientations[o][0]) + ' ' + \ - str(orientations[o][1]) + ' ' + str(orientations[o][2]) + '\n' - - #masses, gfactors + for param in self.required_params: + if param not in self.as_dict(): + warnings.warn( + "Required parameter '{}' not specified!".format(param)) - full_string = '&allocations\n'+indent+'nelements='+nelements+',\n' - full_string += indent+'natoms='+natoms+',\n' - full_string += indent+'ngrid(:)='+str(ngrid[0])+' '+str(ngrid[1])+' '+str(ngrid[2])+'\n' - full_string += indent+'norientations='+norientations+'\n' + alloc_dict = {k: self[k] for k in self.allocations_keys + if k in self and self[k] is not None} + alloc_nml = f90nml.Namelist({"allocations": alloc_dict}) + control_str = str(alloc_nml) + "\n" - full_string += '&end\n&crystal\n' - full_string += indent+'lfactor='+lfactor+',\n' - full_string += indent+'lattvec(:,1)='+str(lattvec1[0])+' '+str(lattvec1[1])+' '+str(lattvec1[2])+',\n' - full_string += indent+'lattvec(:,2)='+str(lattvec2[0])+' '+str(lattvec2[1])+' '+str(lattvec2[2])+',\n' - full_string += indent+'lattvec(:,3)='+str(lattvec3[0])+' '+str(lattvec3[1])+' '+str(lattvec3[2])+',\n' - full_string += indent+'elements=' - if isinstance(elements, list): - for i in range(len(elements)): - full_string += '\"'+elements[i]+str('\"') - if i != (len(elements)-1): - full_string += ' ' - else: - full_string += '\n' - else: - full_string += '\"'+elements+str('\"\n') - full_string += indent+types_string - full_string += positions_string - full_string += indent+'epsilon(:,1)='+str(epsilon1[0])+' '+str(epsilon1[1])+' '+str(epsilon1[2])+',\n' - full_string += indent+'epsilon(:,2)='+str(epsilon2[0])+' '+str(epsilon2[1])+' '+str(epsilon2[2])+',\n' - full_string += indent+'epsilon(:,3)='+str(epsilon3[0])+' '+str(epsilon3[1])+' '+str(epsilon3[2])+',\n' - full_string += born_string - full_string += indent+'scell(:)='+str(scell[0])+' '+str(scell[1])+' '+str(scell[2])+'\n' - full_string += orientations_string + crystal_dict = {k: self[k] for k in self.crystal_keys + if k in self and self[k] is not None} + crystal_nml = f90nml.Namelist({"crystal": crystal_dict}) + control_str += str(crystal_nml) + "\n" - full_string += '&end\n¶meters\n' - full_string += indent+'T='+temperature+'\n' - full_string += indent+'scalebroad='+scalebroad+'\n' - full_string += indent+'T_min='+t_min+'\n' - full_string += indent+'T_max='+t_max+'\n' - full_string += indent+'T_step='+t_step+'\n' - full_string += indent+'omega_max='+omega_max+'\n' - full_string += indent+'rmin='+rmin+'\n' - full_string += indent+'rmax='+rmax+'\n' - full_string += indent+'dr='+dr+'\n' - full_string += indent+'maxiter='+maxiter+'\n' - full_string += indent+'nticks='+nticks+'\n' - full_string += indent+'eps='+eps+'\n' + params_dict = {k: self[k] for k in self.params_keys + if k in self and self[k] is not None} + params_nml = f90nml.Namelist({"params": params_dict}) + control_str += str(params_nml) + "\n" - full_string += '&end\n&flags\n' - full_string += indent+'isotopes='+boolean_to_string(isotopes)+'\n' - full_string += indent+'onlyharmonic='+boolean_to_string(onlyharmonic)+'\n' - full_string += indent+'nonanalytic='+boolean_to_string(nonanalytic)+'\n' - full_string += indent+'nanowires='+boolean_to_string(nanowires)+'\n' - full_string += indent + 'convergence=' + boolean_to_string(convergence) + '\n' - full_string += indent + 'autoisotopes=' + boolean_to_string(autoisotopes) + '\n' - full_string += indent + 'espresso=' + boolean_to_string(espresso) + '\n' - full_string += '&end' + flags_dict = {k: self[k] for k in self.flags_keys + if k in self and self[k] is not None} + flags_nml = f90nml.Namelist({"flags": flags_dict}) + control_str += str(flags_nml) - def remove_substring(substring, string): - #Removes lines from 'string' containing 'substring' - return re.sub('.*'+substring+'.*\n?', '', string) + with open(filename, "w") as file: + file.write(control_str) - full_string = remove_substring('None', full_string) - file = open(filename, 'w+') - file.write(full_string) - file.close() \ No newline at end of file + def as_dict(self): + return dict(self) diff --git a/pymatgen/io/tests/test_shengbte.py b/pymatgen/io/tests/test_shengbte.py index b6c8cd5e0c8..5fd94bf2a54 100644 --- a/pymatgen/io/tests/test_shengbte.py +++ b/pymatgen/io/tests/test_shengbte.py @@ -47,34 +47,30 @@ def setUp(self): def test_from_file(self): io = Control.from_file(self.filename) - self.assertIsInstance(io.alloc_dict, dict) - self.assertIsInstance(io.crystal_dict, dict) - self.assertIsInstance(io.params_dict, dict) - self.assertIsInstance(io.flags_dict, dict) - self.assertEqual(io.alloc_dict['nelements'], 1) - self.assertEqual(io.alloc_dict['natoms'], 2) - self.assertArrayEqual(io.alloc_dict['ngrid'], [25, 25, 25]) - self.assertEqual(io.alloc_dict['norientations'], 0) - self.assertEqual(io.crystal_dict['lfactor'], 0.1) - self.assertEqual(io.crystal_dict['lattvec'][0], [0.0, 2.734363999, 2.734363999]) - self.assertEqual(io.crystal_dict['lattvec'][1], [2.734363999, 0.0, 2.734363999]) - self.assertEqual(io.crystal_dict['lattvec'][2], [2.734363999, 2.734363999, 0.0]) - self.assertIsInstance(io.crystal_dict['elements'], (list, str)) - if isinstance(io.crystal_dict['elements'], list): - all_strings = all(isinstance(item, str) for item in io.crystal_dict['elements']) + self.assertEqual(io['nelements'], 1) + self.assertEqual(io['natoms'], 2) + self.assertArrayEqual(io['ngrid'], [25, 25, 25]) + self.assertEqual(io['norientations'], 0) + self.assertEqual(io['lfactor'], 0.1) + self.assertEqual(io['lattvec'][0], [0.0, 2.734363999, 2.734363999]) + self.assertEqual(io['lattvec'][1], [2.734363999, 0.0, 2.734363999]) + self.assertEqual(io['lattvec'][2], [2.734363999, 2.734363999, 0.0]) + self.assertIsInstance(io['elements'], (list, str)) + if isinstance(io['elements'], list): + all_strings = all(isinstance(item, str) for item in io['elements']) self.assertTrue(all_strings) - self.assertIsInstance(io.crystal_dict['types'], (list, int)) - if isinstance(io.crystal_dict['types'], list): - all_ints = all(isinstance(item, int) for item in io.crystal_dict['types']) + self.assertIsInstance(io['types'], (list, int)) + if isinstance(io['types'], list): + all_ints = all(isinstance(item, int) for item in io['types']) self.assertTrue(all_ints) - self.assertArrayEqual(io.crystal_dict['positions'], [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]) - self.assertArrayEqual(io.crystal_dict['scell'], [5, 5, 5]) - self.assertEqual(io.params_dict['T'], 500) - self.assertEqual(io.params_dict['scalebroad'], 0.5) - self.assertFalse(io.flags_dict['isotopes']) - self.assertFalse(io.flags_dict['onlyharmonic']) - self.assertFalse(io.flags_dict['nonanalytic']) - self.assertFalse(io.flags_dict['nanowires']) + self.assertArrayEqual(io['positions'], [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]) + self.assertArrayEqual(io['scell'], [5, 5, 5]) + self.assertEqual(io['T'], 500) + self.assertEqual(io['scalebroad'], 0.5) + self.assertFalse(io['isotopes']) + self.assertFalse(io['onlyharmonic']) + self.assertFalse(io['nonanalytic']) + self.assertFalse(io['nanowires']) if os.path.exists(os.path.join(test_dir,'test_control')): os.remove(os.path.join(test_dir,'test_control')) @@ -99,4 +95,11 @@ def test_from_dict(self): self.assertMultiLineEqual(test_string, reference_string) os.remove(os.path.join(test_dir, 'test_control')) + def test_MSONable_implementation(self): + # tests as dict and from dict methods + Controlinfromfile = Control.from_file(self.filename) + newControlin = Control.from_dict(Controlinfromfile.as_dict()) + self.assertDictEqual(newControlin, Controlinfromfile) + newControlin.to_json() + diff --git a/requirements-optional.txt b/requirements-optional.txt index 1182643342f..89175f19500 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -13,3 +13,4 @@ networkx==2.2 pygraphviz==1.3.1 h5py==2.7.1 #BoltzTraP2==18.9.1 +f90nml==1.1.2 From dd60b5b3d2aecd8c43e8e8acb295a2223e8b5aed Mon Sep 17 00:00:00 2001 From: reeschang Date: Mon, 8 Jul 2019 21:46:48 -0700 Subject: [PATCH 06/11] Updated required_params and doc strings --- pymatgen/io/shengbte.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index e329be5e670..0139242e4a7 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -29,8 +29,8 @@ class Control(dict, MSONable): Class for reading, updating, and writing ShengBTE CONTROL files. """ - required_params = ["lattvec", "types", "elements", "positions", "scell", - "t", "scalebroad"] + required_params = ["nelements", "natoms", "ngrid", "lattvec", "types", + "elements", "positions", "scell"] allocations_keys = ["nelements", "natoms", "ngrid", "norientations"] crystal_keys = ["lfactor", "lattvec", "types", "elements", "positions", "masses", "gfactors", "epsilon", "born", "scell", @@ -47,14 +47,22 @@ def __init__(self, ngrid=None, lfactor=0.1, scalebroad=0.5, t=500, **kwargs): """ See https://bitbucket.org/sousaw/shengbte/src/master/ for more - detailed description of CONTROL arguments. + detailed description and default values of CONTROL arguments. Args: - ngrid (size 3 list): - lfactor (float): - scell (size 3 list): - scalebroad (float): - t (int): Temperature (Kelvin) + nelements (int): number of different elements in the compound + natoms (int): number of atoms in the unit cell + ngrid (size 3 list): number of grid planes along each axis in + reciprocal space + lattvec (size 3x3 array): real-space lattice vectors, in units of + lfactor + types (size natom list): a vector of natom integers, ranging from 1 + to nelements, assigning an element to each atom in the system + elements (size natom list): a vector of element names + positions (size natomx3 array): atomic positions in lattice + coordinates + scell (size 3 list): supercell sizes along each crystal axis used + for the 2nd-order force constant calculation **kwargs: Other ShengBTE parameters. """ if ngrid is None: From 18bfe2a01fb50bdd6c10afd06b2f6850b0d6acbb Mon Sep 17 00:00:00 2001 From: reeschang Date: Tue, 9 Jul 2019 10:15:08 -0700 Subject: [PATCH 07/11] Fixed bugs in ShengBTE IO unit tests --- pymatgen/io/shengbte.py | 37 ++++++++++++++--------- pymatgen/io/tests/test_shengbte.py | 42 ++++++++++++-------------- test_files/shengbte/CONTROL-CSLD_Si | 46 ++++++++++++++--------------- 3 files changed, 65 insertions(+), 60 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index 0139242e4a7..3e34ed22be5 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -44,26 +44,34 @@ class Control(dict, MSONable): "ShengBTE Control object requires f90nml to be installed. " \ "Please get it at https://pypi.org/project/f90nml.") def __init__(self, ngrid=None, lfactor=0.1, - scalebroad=0.5, t=500, **kwargs): + scalebroad=0.5, t=300, **kwargs): """ See https://bitbucket.org/sousaw/shengbte/src/master/ for more detailed description and default values of CONTROL arguments. Args: - nelements (int): number of different elements in the compound - natoms (int): number of atoms in the unit cell ngrid (size 3 list): number of grid planes along each axis in reciprocal space - lattvec (size 3x3 array): real-space lattice vectors, in units of - lfactor - types (size natom list): a vector of natom integers, ranging from 1 - to nelements, assigning an element to each atom in the system - elements (size natom list): a vector of element names - positions (size natomx3 array): atomic positions in lattice - coordinates - scell (size 3 list): supercell sizes along each crystal axis used - for the 2nd-order force constant calculation - **kwargs: Other ShengBTE parameters. + lfactor (float): unit of measurement for lattice vectors (nm) + scalebroad (float): scale parameter for Gaussian smearing. A value + of 1.0 is theoretically guaranteed to work, but significant + speedups can sometimes be achieved by reducing it with + negligible loss of precision. + t (int or float): temperature (Kelvin) + **kwargs: Other ShengBTE parameters. Several parameters are required + for ShengBTE to run - we have listed these parameters below: + - nelements (int): number of different elements in the compound + - natoms (int): number of atoms in the unit cell + - lattvec (size 3x3 array): real-space lattice vectors, in units + of lfactor + - types (size natom list): a vector of natom integers, ranging + from 1 to nelements, assigning an element to each atom in the + system + - elements (size natom list): a vector of element names + - positions (size natomx3 array): atomic positions in lattice + coordinates + - scell (size 3 list): supercell sizes along each crystal axis + used for the 2nd-order force constant calculation """ if ngrid is None: ngrid = [25, 25, 25] @@ -119,6 +127,7 @@ def to_file(self, filename): if param not in self.as_dict(): warnings.warn( "Required parameter '{}' not specified!".format(param)) + raise AttributeError("param {} not specified".format(param)) alloc_dict = {k: self[k] for k in self.allocations_keys if k in self and self[k] is not None} @@ -132,7 +141,7 @@ def to_file(self, filename): params_dict = {k: self[k] for k in self.params_keys if k in self and self[k] is not None} - params_nml = f90nml.Namelist({"params": params_dict}) + params_nml = f90nml.Namelist({"parameters": params_dict}) control_str += str(params_nml) + "\n" flags_dict = {k: self[k] for k in self.flags_keys diff --git a/pymatgen/io/tests/test_shengbte.py b/pymatgen/io/tests/test_shengbte.py index 5fd94bf2a54..3b119b2e1d3 100644 --- a/pymatgen/io/tests/test_shengbte.py +++ b/pymatgen/io/tests/test_shengbte.py @@ -21,28 +21,24 @@ class TestShengBTE(PymatgenTest): def setUp(self): self.filename = os.path.join(test_dir, "CONTROL-CSLD_Si") self.test_dict = { - 'allocations': - {'nelements': 1, - 'natoms': 2, - 'ngrid': [25, 25, 25], - 'norientations': 0}, - 'crystal': - {'lfactor': 0.1, - 'lattvec': [[0.0, 2.734363999, 2.734363999], - [2.734363999, 0.0, 2.734363999], - [2.734363999, 2.734363999, 0.0]], - 'elements': 'Si', - 'types': [1, 1], - 'positions': [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]], - 'scell': [5, 5, 5]}, - 'parameters': - {'T': 500, - 'scalebroad': 0.5}, - 'flags': - {'isotopes': False, - 'onlyharmonic': False, - 'nonanalytic': False, - 'nanowires': False} + 'nelements': 1, + 'natoms': 2, + 'ngrid': [25, 25, 25], + 'norientations': 0, + 'lfactor': 0.1, + 'lattvec': [[0.0, 2.734363999, 2.734363999], + [2.734363999, 0.0, 2.734363999], + [2.734363999, 2.734363999, 0.0]], + 'elements': 'Si', + 'types': [1, 1], + 'positions': [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]], + 'scell': [5, 5, 5], + 't': 500, + 'scalebroad': 0.5, + 'isotopes': False, + 'onlyharmonic': False, + 'nonanalytic': False, + 'nanowires': False } def test_from_file(self): @@ -65,7 +61,7 @@ def test_from_file(self): self.assertTrue(all_ints) self.assertArrayEqual(io['positions'], [[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]) self.assertArrayEqual(io['scell'], [5, 5, 5]) - self.assertEqual(io['T'], 500) + self.assertEqual(io['t'], 500) self.assertEqual(io['scalebroad'], 0.5) self.assertFalse(io['isotopes']) self.assertFalse(io['onlyharmonic']) diff --git a/test_files/shengbte/CONTROL-CSLD_Si b/test_files/shengbte/CONTROL-CSLD_Si index 8d5f46d81d1..e8ebc1d6bda 100644 --- a/test_files/shengbte/CONTROL-CSLD_Si +++ b/test_files/shengbte/CONTROL-CSLD_Si @@ -1,27 +1,27 @@ &allocations - nelements=1, - natoms=2, - ngrid(:)=25 25 25 - norientations=0 -&end + natoms = 2 + nelements = 1 + ngrid = 25, 25, 25 + norientations = 0 +/ &crystal - lfactor=0.1, - lattvec(:,1)=0.0 2.734363999 2.734363999, - lattvec(:,2)=2.734363999 0.0 2.734363999, - lattvec(:,3)=2.734363999 2.734363999 0.0, - elements="Si" - types=1 1, - positions(:,1)=0.0 0.0 0.0, - positions(:,2)=0.25 0.25 0.25, - scell(:)=5 5 5 -&end + elements = 'Si' + lattvec(:,1) = 0.0, 2.734363999, 2.734363999 + lattvec(:,2) = 2.734363999, 0.0, 2.734363999 + lattvec(:,3) = 2.734363999, 2.734363999, 0.0 + lfactor = 0.1 + positions(:,1) = 0.0, 0.0, 0.0 + positions(:,2) = 0.25, 0.25, 0.25 + scell = 5, 5, 5 + types = 1, 1 +/ ¶meters - T=500 - scalebroad=0.5 -&end + scalebroad = 0.5 + t = 500 +/ &flags - isotopes=.FALSE. - onlyharmonic=.FALSE. - nonanalytic=.FALSE. - nanowires=.FALSE. -&end \ No newline at end of file + isotopes = .false. + nanowires = .false. + nonanalytic = .false. + onlyharmonic = .false. +/ \ No newline at end of file From f61864e2c9a26990164af39205ff66684c4f6a2d Mon Sep 17 00:00:00 2001 From: reeschang Date: Tue, 9 Jul 2019 10:29:30 -0700 Subject: [PATCH 08/11] Fixed 'from_file()' to be a class method --- pymatgen/io/shengbte.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index 3e34ed22be5..b121438c223 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -82,8 +82,8 @@ def __init__(self, ngrid=None, lfactor=0.1, self["t"] = t self.update(kwargs) - @staticmethod - def from_file(filepath): + @classmethod + def from_file(cls, filepath): """ Read a CONTROL namelist file and output a 'Control' object @@ -102,7 +102,7 @@ def from_file(filepath): all_dict.update(sdict["parameters"]) all_dict.update(sdict["flags"]) - return Control(**all_dict) + return cls.from_dict(all_dict) @classmethod def from_dict(cls, sdict): From e61232c3c6f116444e0ed0f4dc6e4fd01d4dffb7 Mon Sep 17 00:00:00 2001 From: reeschang Date: Tue, 9 Jul 2019 11:07:11 -0700 Subject: [PATCH 09/11] Added unittest skip if f90nml is not installed --- pymatgen/io/tests/test_shengbte.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pymatgen/io/tests/test_shengbte.py b/pymatgen/io/tests/test_shengbte.py index 3b119b2e1d3..d3d63fb323f 100644 --- a/pymatgen/io/tests/test_shengbte.py +++ b/pymatgen/io/tests/test_shengbte.py @@ -15,7 +15,12 @@ this_dir = os.path.dirname(os.path.abspath(__file__)) +try: + import f90nml +except ImportError: + f90nml = None +@unittest.skipIf(not f90nml, "f90nml not present. Skipping...") class TestShengBTE(PymatgenTest): def setUp(self): From 4dfd72ee7b5ad3ffedfd52fd8d28e7599a38a595 Mon Sep 17 00:00:00 2001 From: reeschang Date: Tue, 9 Jul 2019 11:08:27 -0700 Subject: [PATCH 10/11] Minor edit --- pymatgen/io/shengbte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index b121438c223..4190227395e 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -41,7 +41,7 @@ class Control(dict, MSONable): "nanowires", "onlyharmonic", "espresso"] @requires(f90nml, - "ShengBTE Control object requires f90nml to be installed. " \ + "ShengBTE Control object requires f90nml to be installed. " "Please get it at https://pypi.org/project/f90nml.") def __init__(self, ngrid=None, lfactor=0.1, scalebroad=0.5, t=300, **kwargs): From 09153cc076b8d8206580980ba83725f6ca33227d Mon Sep 17 00:00:00 2001 From: reeschang Date: Wed, 10 Jul 2019 11:47:04 -0700 Subject: [PATCH 11/11] Minor edits --- pymatgen/io/shengbte.py | 58 +++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/pymatgen/io/shengbte.py b/pymatgen/io/shengbte.py index 4190227395e..bbcc698de9d 100644 --- a/pymatgen/io/shengbte.py +++ b/pymatgen/io/shengbte.py @@ -24,9 +24,34 @@ class Control(dict, MSONable): - """ Class for reading, updating, and writing ShengBTE CONTROL files. + See https://bitbucket.org/sousaw/shengbte/src/master/ for more + detailed description and default values of CONTROL arguments. + + Args: + ngrid (size 3 list): number of grid planes along each axis in + reciprocal space + lfactor (float): unit of measurement for lattice vectors (nm) + scalebroad (float): scale parameter for Gaussian smearing. A value + of 1.0 is theoretically guaranteed to work, but significant + speedups can sometimes be achieved by reducing it with + negligible loss of precision. + t (int or float): temperature (Kelvin) + **kwargs: Other ShengBTE parameters. Several parameters are required + for ShengBTE to run - we have listed these parameters below: + - nelements (int): number of different elements in the compound + - natoms (int): number of atoms in the unit cell + - lattvec (size 3x3 array): real-space lattice vectors, in units + of lfactor + - types (size natom list): a vector of natom integers, ranging + from 1 to nelements, assigning an element to each atom in the + system + - elements (size natom list): a vector of element names + - positions (size natomx3 array): atomic positions in lattice + coordinates + - scell (size 3 list): supercell sizes along each crystal axis + used for the 2nd-order force constant calculation """ required_params = ["nelements", "natoms", "ngrid", "lattvec", "types", @@ -45,34 +70,6 @@ class Control(dict, MSONable): "Please get it at https://pypi.org/project/f90nml.") def __init__(self, ngrid=None, lfactor=0.1, scalebroad=0.5, t=300, **kwargs): - """ - See https://bitbucket.org/sousaw/shengbte/src/master/ for more - detailed description and default values of CONTROL arguments. - - Args: - ngrid (size 3 list): number of grid planes along each axis in - reciprocal space - lfactor (float): unit of measurement for lattice vectors (nm) - scalebroad (float): scale parameter for Gaussian smearing. A value - of 1.0 is theoretically guaranteed to work, but significant - speedups can sometimes be achieved by reducing it with - negligible loss of precision. - t (int or float): temperature (Kelvin) - **kwargs: Other ShengBTE parameters. Several parameters are required - for ShengBTE to run - we have listed these parameters below: - - nelements (int): number of different elements in the compound - - natoms (int): number of atoms in the unit cell - - lattvec (size 3x3 array): real-space lattice vectors, in units - of lfactor - - types (size natom list): a vector of natom integers, ranging - from 1 to nelements, assigning an element to each atom in the - system - - elements (size natom list): a vector of element names - - positions (size natomx3 array): atomic positions in lattice - coordinates - - scell (size 3 list): supercell sizes along each crystal axis - used for the 2nd-order force constant calculation - """ if ngrid is None: ngrid = [25, 25, 25] @@ -118,7 +115,7 @@ def from_dict(cls, sdict): """ return cls(**sdict) - def to_file(self, filename): + def to_file(self, filename='CONTROL'): """ Writes ShengBTE CONTROL file from 'Control' object """ @@ -127,7 +124,6 @@ def to_file(self, filename): if param not in self.as_dict(): warnings.warn( "Required parameter '{}' not specified!".format(param)) - raise AttributeError("param {} not specified".format(param)) alloc_dict = {k: self[k] for k in self.allocations_keys if k in self and self[k] is not None}