# coding: utf-8 """ Module parameters ================= """ import textwrap import numpy as np import re from terminaltables import AsciiTable from msspec.misc import LOGGER, UREG, XRaySource, get_level_from_electron_configuration import ase class Parameter(object): def __init__(self, name, types=None, limits=(None, None), unit=None, allowed_values=None, default=None, pattern=None, fmt='s', binding=None, private=False, group="", doc='Sorry, no more help for this parameter.'): self.name = name self.group = group if isinstance(types, (tuple, list)): self.allowed_types = types else: self.allowed_types = (types,) self.low_limit, self.high_limit = limits self.unit = unit self.allowed_values = allowed_values self.default = default self.pattern = pattern self.fmt = fmt self.binding = binding self.private = private self.docstring = doc self._value = None self.value = default def serialize(self): data = { 'name': self.name, 'types': self.allowed_types, 'limits': (self.low_limit, self.high_limit), 'unit': self.unit, 'allowed_values': self.allowed_values, 'default': self.default, 'pattern': self.pattern, 'fmt': self.fmt, # 'binding': self.binding, 'private': self.private, 'group': self.group, 'doc': self.docstring } return data def convert(self, value): if hasattr(value, 'magnitude'): if self.unit != None: return value.to(self.unit).magnitude return value def check(self, value): def assert_message(msg, *args): s = '\'{}\': {}'.format(self.name, msg) s = s.format(*args) try: allowed_types = '\n'.join( ['- ' + str(_) for _ in self.allowed_types]) except TypeError: allowed_types = self.allowed_types try: allowed_values = '\n'.join( ['- ' + str(_) for _ in self.allowed_values]) except TypeError: allowed_values = self.allowed_values data = [ ['PROPERTY', 'VALUE'], ['Name', '\'{}\''.format(self.name)], ['Allowed types', '{}'.format(allowed_types)], ['Limits', '{} <= value <= {}'.format(self.low_limit, self.high_limit)], ['Unit', '{}'.format(self.unit)], ['Allowed values', '{}'.format(allowed_values)], ['Default', '{}'.format(self.default)] ] t = AsciiTable(data) table = '\n'.join(['\t' * 2 + _ for _ in t.table.split('\n')]) s = "{}\n\n{}\n{}".format(s, table, self.docstring) return s # convert if a unit was given _value = self.convert(value) if self.allowed_types != None: # val = value # if hasattr(value, 'magnitude'): # val = value.magnitude try: if isinstance(_value, bool): assert bool in self.allowed_types assert isinstance(_value, self.allowed_types) except AssertionError: raise AssertionError(assert_message( 'Type {} is not an allowed type for this option ' '({} allowed)', str(type(_value)), str(self.allowed_types))) # if not isinstance(_value, (list, tuple, np.ndarray)): # _values = [_value,] # if isinstance(_value, tuple): # _values = list(_value) # for i, val in enumerate(_values): for val in np.array(_value).flatten(): if self.low_limit != None: assert val >= self.low_limit, assert_message( 'Value must be >= {:s}', str(self.low_limit)) if self.high_limit != None: assert val <= self.high_limit, assert_message( 'Value must be <= {:s}', str(self.high_limit)) if self.allowed_values != None and isinstance(val, tuple(type(x) for x in self.allowed_values)): assert val in self.allowed_values, assert_message( 'This value is not allowed. Please choose between ' 'one of {:s}', str(self.allowed_values)) if self.pattern != None: p = re.compile(self.pattern) m = p.match(val) assert m != None, assert_message( 'Wrong string format') # _value[i] = val return _value @property def value(self): return self._value @value.setter def value(self, value): v = self.check(value) self._value = v # if hasattr(value, 'magnitude'): # self._value = value.magnitude # else: # self._value = value if self.binding: new_value = None try: new_value = self.binding(self) except AttributeError: pass if new_value is not None: self._value = new_value # LOGGER.debug('{:s} = {:s}'.format(self.name, str(self.value))) def get(self, *args): return self.value def set(self, *args): value = args[-1] self.value = value def __str__(self): fmt = '{:' + self.fmt + '}' try: return fmt.format(self._value) except ValueError: return str(self._value) def __len__(self): try: return len(self._value) except TypeError: return 1 class BaseParameters(object): __isfrozen = False def __init__(self): self._parameters = [] def add_parameters(self, *parameters): cls = self.__class__ for p in parameters: p.group = self.__class__.__name__ try: p.binding = getattr(self, 'bind_' + p.name) except AttributeError: pass if not p.private: setattr(self, 'set_' + p.name, p.set) setattr(self, 'get_' + p.name, p.get) setattr(cls, p.name, property(fset=p.set, fget=p.get, doc=p.docstring)) self._parameters.append(p) def get_parameter(self, name): retval = None for p in self._parameters: if p.name == name: retval = p if retval is None: raise KeyError('No such parameter: {}'.format(name)) return retval def set_parameter(self, name, value, force=False): found = False for p in self._parameters: if p.name == name: if p.private: if force: LOGGER.debug("forcing the value of the private " "parameter '%s' to the value = %s", p.name, value) else: err_msg = ("Cannot change the value of the private " "parameter '{}'").format(p.name) LOGGER.error("Cannot change the value of a private " "parameter") raise NameError(err_msg) p.value = value found = True if found is False: err_msg = 'No such parameter: {}'.format(name) LOGGER.error("Unknwon parameter's name!") raise ValueError(err_msg) def freeze(self, frozen=True): self.__isfrozen = frozen def __setattr__(self, key, value): if self.__isfrozen and not hasattr(self, key): data = '\n'.join( ["\t\t- '{}'".format(p.name) for p in self._parameters]) err_msg = """ '{}' is not an allowed attribute of {} class. Please use one of:\n{}""".format(key, self.__class__.__name__, data) print(key, value) LOGGER.error('Unknown attribute!') raise AttributeError(err_msg) object.__setattr__(self, key, value) def __iter__(self): for x in range(len(self._parameters)): yield self._parameters[x] def __getitem__(self, index): return self._parameters[index] class PhagenParameters(BaseParameters): def __init__(self): parameters = ( Parameter('calctype', allowed_values=('xpd', 'xas', 'aed', 'led', 'rex', 'els', 'e2e'#, 'dos' ), types=(str,), default='xpd'), Parameter('expmode', allowed_values=('cis', 'cfs', 'cel'), types=(str,), default='cis'), Parameter('coor', allowed_values=('angs', 'au'), types=(str,), default='angs'), Parameter('enunit', allowed_values=('ryd', 'eV'), fmt='3>s', types=(str,), default='ryd'), Parameter('einc', types=(int, float), limits=(0, None), fmt='.1f', default=700.), Parameter('esct', types=(int, float), limits=(0, None), fmt='.1f', default=580.), Parameter('scangl', types=(int, float), limits=(0, 360), fmt='.2f', default=0.), Parameter('lambda', types=(int, float), fmt='.2f', default=20.), Parameter('emin', types=(int, float), limits=(0, None), fmt='.4f', default=13.5236), Parameter('emax', types=(int, float), limits=(0, None), fmt='.4f', default=13.5236), Parameter('delta', types=(int, float), limits=(0, None), fmt='.4f', default=0.3), Parameter('cip', types=(int, float), limits=(0, None), fmt='.4f', default=0.), Parameter('potgen', allowed_values=('in', 'ex'), types=(str,), default='in'), Parameter('potype', allowed_values=('hdrel', 'hedin', 'xalph', 'dhrel', 'dhcmp', #'lmto', 'msf', 'spkkr' ), types=(str,), fmt='5>s', default='hedin'), Parameter('relc', allowed_values=('nr', 'sr', 'so'), types=(str,), default='nr'), Parameter('norman', allowed_values=('stdcrm', 'scaled', 'extrad'), types=(str,), default='stdcrm'), Parameter('ovlpfac', types=(int, float), limits=(0, None), fmt='.4f', default=0.), Parameter('gamma', types=(int, float), limits=(None, None), fmt='.2f', default=0.03), Parameter('charelx', allowed_values=('ex', 'gs'), types=(str,), default='gs'), Parameter('ionzst', allowed_values=('neutral', 'ionic'), types=(str,), fmt='7>s', default='neutral'), Parameter('eikappr', allowed_values=('yes', 'no'), types=(str,), fmt='3>s', default='no'), Parameter('db', types=(int, float), fmt='.2f', default=0.01), Parameter('optrsh', allowed_values=('y', 'n'), types=(str,), default='n'), Parameter('rsh', types=(float,), limits=(0., None), fmt='.1f', default=0.9), Parameter('lmax_mode', types=(int,), limits=(0, 2), fmt='d', default=2), Parameter('lmaxt', types=(int,), limits=(0, None), fmt='d', default=20), Parameter('edge', types=(str,), fmt='<2s', default='k'), Parameter('edge1', types=(str,), fmt='<2s', default='l1'), Parameter('edge2', types=(str,), fmt='<2s', default='l1'), Parameter('l2h', types=(int,), fmt='d', default=4), Parameter('ionicity', types=dict, default={}), #Parameter('absorber', types=(int,), limits=(1, None), fmt='d', default=1), #Parameter('nosym', types=(str,), allowed_values=('.true.', '.false.'), fmt='s', default='.true.'), #Parameter('outersph', types=(str,), allowed_values=('.true.', '.false.'), fmt='s', default='.false.'), Parameter('atoms', types=ase.atoms.Atoms, default=ase.atoms.Atoms())) BaseParameters.__init__(self) self.add_parameters(*parameters) self.freeze() class PhagenMallocParameters(BaseParameters): def __init__(self): parameters = ( Parameter('nat_', types=int, limits=(1, None), default=1550), Parameter('ua_', types=int, limits=(1, None), default=1550), Parameter('neq_', types=int, limits=(1, None), default=48), Parameter('rdx_', types=int, limits=(1, None), default=1500), Parameter('lmax_', types=int, limits=(1, None), default=60), Parameter('nef_', types=int, limits=(1, None), default=200), Parameter('lexp_', types=int, limits=(1, None), default=10), Parameter('nep_', types=int, limits=(1, None), default=1000), ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.freeze() class SpecParameters(BaseParameters): def __init__(self): parameters = ( Parameter('calctype_spectro', types=str, allowed_values=('PHD', 'AED', 'XAS', 'LED', 'ACS', 'EIG'), default='PHD'), Parameter('calctype_ispin', types=int, limits=(0, 1), default=0, fmt='d'), Parameter('calctype_idichr', types=int, limits=(0, 2), default=0, fmt='d'), Parameter('calctype_ipol', types=int, limits=(-1, 2), default=0, fmt='d'), Parameter('calctype_iamp', types=int, limits=(0, 1), default=1, fmt='d'), Parameter('ped_li', types=str, default='1s'), Parameter('ped_so', types=str, default='1/2'), Parameter('ped_initl', types=int, limits=(-1, 2), default=2, fmt='d'), Parameter('ped_iso', types=int, limits=(-1, 2), default=0, fmt='d'), Parameter('ped_iphi', types=int, limits=(-1, 3), default=3, fmt='d'), Parameter('ped_itheta', types=int, limits=(-1, 3), default=0, fmt='d'), Parameter('ped_ie', types=int, limits=(0, 4), default=0, fmt='d'), Parameter('ped_ifthet', types=int, limits=(0, 1), default=0, fmt='d'), Parameter('ped_nphi', types=int, limits=(1, None), default=360, fmt='d'), Parameter('ped_ntheta', types=int, limits=(1, None), default=45, fmt='d'), Parameter('ped_ne', types=int, limits=(1, None), default=1, fmt='d'), Parameter('ped_nfthet', types=int, limits=(1, None), default=1, fmt='d'), Parameter('ped_phi0', types=float, limits=(0., 360.), default=0., fmt='.2f'), Parameter('ped_theta0', types=float, limits=(-360., 360.), default=-70., fmt='.2f'), Parameter('ped_e0', types=float, limits=(0., None), default=316.4, fmt='.2f'), Parameter('ped_r0', types=float, default=0.5, fmt='.3f'), Parameter('ped_phi1', types=float, limits=(0., 360.), default=0., fmt='.2f'), Parameter('ped_theta1', types=float, limits=(-360., 360.), default=69., fmt='.2f'), Parameter('ped_e1', types=float, limits=[0., None], default=316.4, fmt='.2f'), Parameter('ped_r1', types=float, default=-1.0, fmt='.3f'), Parameter('ped_thlum', types=float, default=-55.0, fmt='.2f'), Parameter('ped_philum', types=float, default=0., fmt='.2f'), Parameter('ped_elum', types=float, default=1253.6, fmt='.2f'), Parameter('ped_imod', types=int, default=1, fmt='d'), Parameter('ped_imoy', types=int, default=0, fmt='d'), Parameter('ped_accept', types=float, default=0., fmt='.2f'), Parameter('ped_ichkdir', types=int, default=0, fmt='d'), Parameter('leed_iphi', types=int, limits=(-1, 3), default=-1, fmt='d'), Parameter('leed_itheta', types=int, limits=(-1, 3), default=0, fmt='d'), Parameter('leed_ie', types=int, limits=(0, 4), default=0, fmt='d'), Parameter('leed_ifthet', types=int, limits=(0, 1), default=0, fmt='d'), Parameter('leed_nphi', types=int, limits=(1, None), default=1, fmt='d'), Parameter('leed_ntheta', types=int, limits=(1, None), default=140, fmt='d'), Parameter('leed_ne', types=int, limits=(1, None), default=1, fmt='d'), Parameter('leed_nfthet', types=int, limits=(1, None), default=1, fmt='d'), Parameter('leed_phi0', types=float, limits=(0., 360.), default=0., fmt='.2f'), Parameter('leed_theta0', types=float, limits=(-360., 360.), default=-70., fmt='.2f'), Parameter('leed_e0', types=float, limits=(0., None), default=100., fmt='.2f'), Parameter('leed_r0', types=float, default=0.5, fmt='.3f'), Parameter('leed_phi1', types=float, limits=(0., 360.), default=0., fmt='.2f'), Parameter('leed_theta1', types=float, limits=(-360., 360.), default=69., fmt='.2f'), Parameter('leed_e1', types=float, limits=[0., None], default=316.4, fmt='.2f'), Parameter('leed_r1', types=float, default=-1.0, fmt='.3f'), Parameter('leed_thini', types=float, default=-55.0, fmt='.2f'), Parameter('leed_phiini', types=float, default=0., fmt='.2f'), Parameter('leed_imod', types=int, default=1, fmt='d'), Parameter('leed_imoy', types=int, default=0, fmt='d'), Parameter('leed_accept', types=float, default=0., fmt='.2f'), Parameter('leed_ichkdir', types=int, default=0, fmt='d'), Parameter('exafs_edge', types=str, default='L1', pattern=r'((K|K1)|[KLMNO][1-9])', fmt='<2s'), Parameter('exafs_initl', types=int, limits=[-1, 2], default=1, fmt='d'), Parameter('exafs_thlum', types=float, default=-55.0, fmt='.2f'), Parameter('exafs_philum', types=float, default=0., fmt='.2f'), Parameter('exafs_ne', types=int, limits=[1, None], default=1, fmt='d'), Parameter('exafs_ekini', types=float, limits=[0, None], default=200., fmt='.2f'), Parameter('exafs_ekfin', types=float, limits=[0, None], default=600., fmt='.2f'), Parameter('exafs_ephini', types=float, limits=[0, None], default=1486.7, fmt='.2f'), Parameter('aed_edgec', types=str, default='L2', pattern=r'((K|K1)|[KLMNO][1-9])', fmt='<2s'), Parameter('aed_edgei', types=str, default='M2', pattern=r'((K|K1)|[KLMNO][1-9])', fmt='<2s'), Parameter('aed_edgea', types=str, default='M2', pattern=r'((K|K1)|[KLMNO][1-9])', fmt='<2s'), Parameter('aed_imult', types=int, limits=[0, 1], default=1, fmt='d'), Parameter('aed_mult', types=str, default='1D2', pattern=r'\d[SPDFG]\d', fmt='s'), Parameter('aed_iphi', types=int, limits=(-1, 3), default=0, fmt='d'), Parameter('aed_itheta', types=int, limits=(-1, 3), default=1, fmt='d'), Parameter('aed_ifthet', types=int, limits=(0, 1), default=0, fmt='d'), Parameter('aed_iint', types=int, limits=(0, 3), default=0, fmt='d'), Parameter('aed_nphi', types=int, limits=(1, None), default=1, fmt='d'), Parameter('aed_ntheta', types=int, limits=(1, None), default=1, fmt='d'), Parameter('aed_nfthet', types=int, limits=(1, None), default=1, fmt='d'), Parameter('aed_phi0', types=float, limits=(0., 360.), default=0., fmt='.2f'), Parameter('aed_theta0', types=float, limits=(-360., 360.), default=45., fmt='.2f'), Parameter('aed_r0', types=float, default=0.5, fmt='.3f'), Parameter('aed_phi1', types=float, limits=(0., 360.), default=0., fmt='.2f'), Parameter('aed_theta1', types=float, limits=(-360., 360.), default=70., fmt='.2f'), Parameter('aed_r1', types=float, default=-1.0, fmt='.3f'), Parameter('aed_imod', types=int, default=1, fmt='d'), Parameter('aed_imoy', types=int, default=0, fmt='d'), Parameter('aed_accept', types=float, default=1., fmt='.2f'), Parameter('aed_ichkdir', types=int, default=0, fmt='d'), Parameter('eigval_ne', types=int, limits=[1, None], default=1, fmt='d'), Parameter('eigval_ekini', types=float, limits=[0., None], default=10., fmt='.2f'), Parameter('eigval_ekfin', types=float, limits=[0., None], default=10., fmt='.2f'), Parameter('eigval_idamp', types=int, limits=[0, 3], default=3, fmt='d'), Parameter('eigval_ispectrum_ne', types=int, limits=[0, 1], default=1, fmt='d'), Parameter('eigval_ipwm', types=int, limits=[-4, 4], default=1, fmt='d'), Parameter('eigval_method', types=str, default='EPSI', allowed_values=['AITK', 'RICH', 'SALZ', 'EPSI', 'EPSG', 'RHOA', 'THET', 'LEGE', 'CHEB', 'OVER', 'DURB', 'DLEV', 'TLEV', 'ULEV', 'VLEV', 'ELEV', 'EULE', 'GBWT', 'VARI', 'ITHE', 'EALG']), Parameter('eigval_acc', types=float, limits=[0., None], default=0.001, fmt='.5f'), Parameter('eigval_expo', types=float, default=1., fmt='.3f'), Parameter('eigval_nmax', types=int, limits=[1, None], default=200, fmt='d'), Parameter('eigval_niter', types=int, limits=[1, None], default=10, fmt='d'), Parameter('eigval_ntable', types=int, limits=[1, None], default=3, fmt='d'), Parameter('eigval_shift', types=float, default=0., fmt='.3f'), Parameter('eigval_ixn', types=int, limits=[1, 5], default=1, fmt='d'), Parameter('eigval_iva', types=int, limits=[1, 5], default=1, fmt='d'), Parameter('eigval_ign', types=int, limits=[1, 7], default=1, fmt='d'), Parameter('eigval_iwn', types=int, limits=[1, 6], default=1, fmt='d'), Parameter('eigval_l', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('eigval_alpha', types=float, default=1., fmt='.2f'), Parameter('eigval_beta', types=float, default=1., fmt='.2f'), Parameter('calc_no', types=int, limits=[0, 8], default=1, fmt='d'), Parameter('calc_ndif', types=int, limits=[1, 10], default=3, fmt='d'), Parameter('calc_ispher', types=int, limits=[0, 1], default=1, fmt='d'), Parameter('calc_igr', types=int, limits=[0, 2], default=0, fmt='d'), Parameter('calc_isflip', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_irdia', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_itrtl', types=int, limits=[1, 9], default=7, fmt='d'), Parameter('calc_itest', types=int, limits=[0, 2], default=0, fmt='d'), Parameter('calc_isom', types=int, limits=[0, 2], default=0, fmt='d'), Parameter('calc_nonvol', types=int, limits=[0, 1], default=1, fmt='d'), Parameter('calc_npath', types=int, limits=[0, None], default=100, fmt='d'), Parameter('calc_vint', types=float, default=0., fmt='.2f'), Parameter('calc_ifwd', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_nthout', types=int, limits=[0, None], default=1, fmt='d'), Parameter('calc_ino', types=int, limits=[0, None], default=0, fmt='d'), Parameter('calc_ira', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_ipw', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_ncut', types=int, limits=[0, 10], default=2, fmt='d'), Parameter('calc_pctint', types=float, limits=[1e-4, 999.9999], default=0.01, fmt='.4f'), Parameter('calc_ipp', types=int, limits=[1, 2], default=1, fmt='d'), Parameter('calc_ilength', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_rlength', types=float, limits=[0, None], default=10., fmt='.2f'), Parameter('calc_unlength', types=str, allowed_values=['ANG', ], default='ANG'), Parameter('calc_idwsph', types=int, limits=[0, 1], default=0, fmt='d'), Parameter('calc_ispeed', types=int, limits=[None, 1], default=1, fmt='d'), Parameter('calc_iatt', types=int, limits=[0, 1], default=1, fmt='d'), Parameter('calc_iprint', types=int, limits=[0, 2], default=1, fmt='d'), Parameter('calc_idcm', types=int, limits=[0, 2], default=0, fmt='d'), Parameter('calc_td', types=float, limits=[0., None], default=420., fmt='.2f'), Parameter('calc_t', types=float, limits=[0., None], default=293., fmt='.2f'), Parameter('calc_rsj', types=float, limits=[0., None], default=1.2, fmt='.2f'), Parameter('calc_ilpm', types=int, limits=[-1, 2], default=2, fmt='d'), Parameter('calc_xlpm0', types=float, limits=[0., None], default=15., fmt='.2f'), Parameter('input_data', types=str, default='../input/spec.dat'), Parameter('input_unit00', types=int, default=5, fmt='d'), Parameter('input_tl', types=str, default='../output/tmatrix.tl'), Parameter('input_unit01', types=int, default=1, fmt='d'), Parameter('input_rad', types=str, default='../output/tmatrix.rad'), Parameter('input_unit02', types=int, default=3, fmt='d'), Parameter('input_cluster', types=str, default='../output/cluster.clu'), Parameter('input_unit03', types=int, default=4, fmt='d'), Parameter('input_adsorbate', types=str, default='../input/adsorbate.pos'), Parameter('input_unit04', types=int, default=2, fmt='d'), Parameter('input_kdirs', types=str, default='../input/kdirs.dat'), Parameter('input_unit05', types=int, default=11, fmt='d'), Parameter('input2_tl', types=str, default='../output/tmatrix.tl'), Parameter('input2_unit06', types=int, default=12, fmt='d'), Parameter('input2_rad', types=str, default='../output/tmatrix.rad'), Parameter('input2_unit07', types=int, default=13, fmt='d'), Parameter('input2_kdirs', types=str, default='../input/kdirs.dat'), Parameter('input2_unit08', types=int, default=14, fmt='d'), Parameter('output_log', types=str, default='../output/spec.log'), Parameter('output_unit09', types=int, default=6, fmt='d'), Parameter('output_res', types=str, default='../output/results.dat'), Parameter('output_unit10', types=int, default=9, fmt='d'), Parameter('output_sf', types=str, default='../output/facdif1.dat'), Parameter('output_unit11', types=int, default=8, fmt='d'), Parameter('output_augclus', types=str, default='../output/augclus.clu'), Parameter('output_unit12', types=int, default=10, fmt='d'), Parameter('extra_atoms', types=ase.atoms.Atoms, default=ase.atoms.Atoms()), Parameter('extra_nat', types=int, limits=[1, None], default=1, fmt='d'), Parameter('extra_energies', types=(list, tuple), default=[0., ]), Parameter('extra_nlmax', types=int, default=10), Parameter('extra_level', types=str, default='1s'), Parameter('extra_parameters', types=dict, default={}) ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.freeze() class SpecMallocParameters(BaseParameters): def __init__(self): parameters = ( Parameter('NATP_M', types=int, limits=(1, None), default=20), Parameter('NATCLU_M', types=int, limits=(1, None), default=300), Parameter('NAT_EQ_M', types=int, limits=(1, None), default=16), Parameter('N_CL_L_M', types=int, limits=(1, None), default=1), Parameter('NE_M', types=int, limits=(1, None), default=100), Parameter('NL_M', types=int, limits=(1, None), default=50), Parameter('LI_M', types=int, limits=(1, None), default=7), Parameter('NEMET_M', types=int, limits=(1, None), default=2), Parameter('NO_ST_M', types=int, limits=(1, None), default=2), Parameter('NDIF_M', types=int, limits=(1, None), default=10), Parameter('NSO_M', types=int, limits=(1, None), default=2), Parameter('NTEMP_M', types=int, limits=(1, None), default=1), Parameter('NODES_EX_M', types=int, limits=(1, None), default=3), Parameter('NSPIN_M', types=int, limits=(1, None), default=1), Parameter('NTH_M', types=int, limits=(1, None), default=2000), Parameter('NPH_M', types=int, limits=(1, None), default=2000), Parameter('NDIM_M', types=int, limits=(1, None), default=100000), Parameter('N_TILT_M', types=int, limits=(1, None), default=11), Parameter('N_ORD_M', types=int, limits=(1, None), default=200), Parameter('NPATH_M', types=int, limits=(1, None), default=500), Parameter('NGR_M', types=int, limits=(1, None), default=10), ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.freeze() class GlobalParameters(BaseParameters): def __init__(self, phagen_parameters=None, spec_parameters=None): parameters = ( Parameter('spectroscopy', types=str, allowed_values=( 'PED', 'AED', 'LEED', 'EXAFS', 'EIG'), default='PED', doc=textwrap.dedent(""" There are 4 choices for the spectroscopy option: - '**PED**', for Photo Electron Diffraction - '**AED**', for Auger Electron Diffraction - '**LEED**', for Low Energy Electron Diffraction - '**EXAFS**', for the Extended X-ray Absorption Fine Structure Additionally, a 5th keyword **EIG** is used to get deeper information about the convergence of the eigen values of multiple scattering matrix. The value is case insensitive. """)), Parameter('algorithm', types=str, allowed_values=('expansion', 'inversion', 'correlation', 'power'), default='expansion', doc=textwrap.dedent(""" You can choose the algorithm used for the computation of the scattering path operator. - '**inversion**', for the classical matrix inversion method - '**expansion**', for the Rehr-Albers series expansion - '**correlation**', for the correlation-expansion algorithm - '**power**', for the power method approximation scheme (only for spectroscopy='EIG') The series expansion algorithm is well suited for high energy since the number of terms required decreases as the energy increases. The exact solution is obtained by the matrix inversion method but should be preferably used for lower energy. """)), Parameter('polarization', types=(type(None), str), allowed_values=(None, 'linear_qOz', 'linear_xOy', 'circular'), default=None, doc=textwrap.dedent(""" Specify the polarization of the incident light. - **None**, for unpolarized light - '**linear_qOz**' for a polarization vector in the :math:`(\\vec{q}0z)` plane - '**linear_xOy**' for a polarization vector in the :math:`(x0y)` plane - '**circular**' for circular dichroism """)), Parameter('dichroism', types=(type(None), str), allowed_values=(None, 'natural', 'sum_over_spin', 'spin_resolved'), default=None, doc=textwrap.dedent(""" Used to perform dichroic calculations. The default (None) is to disable this. """)), Parameter('spinpol', types=bool, default=False, doc=textwrap.dedent(""" Enable or disbale spin-resolved calculations. """)), Parameter('folder', types=str, default='./calc', doc=textwrap.dedent(""" This parameter is the path to the temporary folder used for the calculations. If you do not change this parameter between calculations, all the content will be overridden. This is usually not a problem, since the whole bunch of data created during a computation is not meant to be saved. But you may want to anyway by changing it to another path. This folder is not automatically removed after a computation. It is removed when calling the :meth:`shutdown` calculator method: .. code-block:: python calc = MSSPEC() # the './calc' folder is created # do your calculation here calc.shutdown() # the folder is removed """)) ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters self.spec_parameters.extra_parameters['global'] = self self.freeze() def bind_spectroscopy(self, p): mapping = { 'PED': ('xpd', 'PHD'), 'AED': ('aed', 'AED'), 'LEED': ('led', 'LED'), 'EXAFS': ('xas', 'XAS'), 'EIG': ('xpd', 'EIG'), } phagen_calctype, spec_calctype = mapping[p.value] self.phagen_parameters.calctype = phagen_calctype self.spec_parameters.calctype_spectro = spec_calctype class MuffintinParameters(BaseParameters): def __init__(self, phagen_parameters, spec_parameters): parameters = ( Parameter('charge_relaxation', types=bool, default=True, doc=textwrap.dedent(""" Used to specify wether the charge density of the photoabsorbing atom, which is used to construct the potential, is allowaed to relax around the core hole or not. """)), Parameter('ionicity', types=dict, default={}, doc=textwrap.dedent(""" A dictionary to specify the ionicity of each kind of atoms. If empty, the atoms are considered to be neutral. Otherwise, each key must be a chemical symbol and the corresponding value should be the fraction of electrons added (negative) or substracted (positive) with respect to neutrality. As an example for a LaFeO\ :sub:`3` cluster:: >>> calc.muffintin_parameters.ionicity = {'La': 0.15, 'Fe': 0.15, 'O': -0.1} means that 0.15 electrons have been substracted from La, likewise from Fe. Neutrality implies that 0.3 electrons have to be added to oxygen atoms. """)), Parameter('relativistic_mode', types=str, allowed_values=('non_relativistic', 'scalar_relativistic', 'spin_orbit_resolved'), default='non_relativistic', doc=textwrap.dedent(""" To tell whether to use the scalar relativistic approximation or not. """)), Parameter('radius_overlapping', types=float, limits=(0., 1.), default=0., doc=textwrap.dedent(""" to allow atomic spheres to overlapp with each other. The value is a percentage, 1. means 100%. """)), Parameter('interstitial_potential', types=(int, float), unit=UREG.eV, default=0., doc=textwrap.dedent(""" The average interstitial potential (or inner potential) expressed in eV. It is used to compute the refraction at the surface. """)), Parameter('hydrogen_radius', types=(int, float), default=0.9, limits=(0., None), unit=UREG.angstroms, doc=textwrap.dedent(""" The program can have difficulties to find the radius of the hydrogen atom (small atom). You can specify here a value for the radius. If you set it to 'None', the calculation of the muffin-tin radius of H atoms will be left to the program. """)) ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters self.freeze() def bind_charge_relaxation(self, p): mapping = {True: 'ex', False: 'gs'} self.phagen_parameters.charelx = mapping[p.value] def bind_ionicity(self, p): self.phagen_parameters.ionicity = p.value def bind_relativistic_mode(self, p): mapping = {'non_relativistic': 'nr', 'scalar_relativistic': 'sr', 'spin_orbit_resolved': 'so'} self.phagen_parameters.relc = mapping[p.value] def bind_radius_overlapping(self, p): self.phagen_parameters.ovlpfac = p.value def bind_interstitial_potential(self, p): self.spec_parameters.calc_vint = p.value def bind_hydrogen_radius(self, p): self.phagen_parameters.rsh = p.value class TMatrixParameters(BaseParameters): def __init__(self, phagen_parameters): parameters = ( Parameter('potential', types=str, allowed_values=('muffin_tin', 'lmto'), default='muffin_tin', doc=textwrap.dedent(""" This option allows to choose which kind of potential is used to compute the T-Matrix. For now, only the internal *Muffin-Tin* potential is supported. """)), Parameter('exchange_correlation', types=str, allowed_values=('hedin_lundqvist_real', 'hedin_lundqvist_complex', 'x_alpha_real', 'dirac_hara_real', 'dirac_hara_complex'), default='hedin_lundqvist_complex', doc=textwrap.dedent(""" Set the type of exchange and correlation potential that will be used. """)), Parameter('imaginery_part', types=(int, float), default=0., doc=textwrap.dedent(""" This value is added to complex potentials to account for core-hole lifetime and experimental resolution broadening effects. """)), Parameter('lmax_mode', types=str, allowed_values=('max_ke', 'true_ke', 'imposed'), default='true_ke', doc=textwrap.dedent(""" This allows to control the number of basis functions used to expand the wave function on each atom. It can be: - '**imposed**', and will be equal to the *lmaxt* parameter (see below). It is therefore independent on both the energy and atom type. - '**max_ke**', in this case :math:`l_{max}` is computed according to the formula :math:`l_{max} = kr_{at} + 1` where :math:`k=E^{1/2}_{max}` with :math:`E_{max}` being the maximum kinetic energy. In this case :math:`l_{max}` is independent of the energy but depends on the atom number. - '**true_ke**', in this case :math:`l_{max}` is computed according to the formula :math:`l_{max} = kr_{at} + 1` where :math:`k=E^{1/2}_k` with :math:`E_k` being the kinetic energy. In this case :math:`l_{max}` depends both on the energy and the atom number. """)), Parameter('lmaxt', types=int, limits=(1, None), default=19, doc=textwrap.dedent(""" The value of :math:`l_{max}` if *lmax_mode = 'imposed'* """)), Parameter('tl_threshold', types=(type(None), float), default=None, doc=textwrap.dedent(""" This option allows to control the number of basis function by defining a threshold value for the *tl*. For example:: >>> calc.tmatrix_parameters.tl_threshold = 1e-6 will remove all *tl* with a value :math:`< 1.10^{-6}` .. note:: This option is compatible with any modes of the *lmax_mode* option. """)), Parameter('max_tl', types=(type(None), dict), default=None, doc=textwrap.dedent(""" *max_tl* is used to sepcify a maximum number of basis functions to use for each kind of atoms. For example, in the case of an MgO cluster, you could write:: >>> calc.muffintin_parameters.max_tl = {'Mg': 20, 'O', 15} to tell the program to use at most 20 *tl* for Mg and 15 for O. It acts like a filter, meaning that if you use this option, you are not required to sepcif a value for each kind of atoms in your cluster. You can restrict the number of *tl* only for one type of atom for example. .. note:: This option is compatible with any modes of the *lmax_mode* option. """)) ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.phagen_parameters = phagen_parameters self.freeze() def bind_potential(self, p): mapping = {'muffin_tin': 'in', 'lmto': 'ex'} self.phagen_parameters.potgen = mapping[p.value] def bind_exchange_correlation(self, p): potential = self.get_parameter('potential').value if potential == 'muffin_tin': mapping = { 'hedin_lundqvist_real': 'hdrel', 'hedin_lundqvist_complex': 'hedin', 'x_alpha_real': 'xalph', 'dirac_hara_real': 'dhrel', 'dirac_hara_complex': 'dhcmp' } self.phagen_parameters.potype = mapping[p.value] elif potential == 'lmto': self.phagen_parameters.potype = 'lmto' def bind_imaginery_part(self, p): self.phagen_parameters.gamma = p.value def bind_lmax_mode(self, p): mapping = { 'imposed': 0, 'max_ke': 1, 'true_ke': 2 } self.phagen_parameters.lmax_mode = mapping[p.value] def bind_lmaxt(self, p): self.phagen_parameters.lmaxt = p.value def bind_max_tl(self, p): cluster = self.phagen_parameters.get_parameter('atoms').value # issue a warning if a chemical symbol entered in max_tl is not # a chemical symbol in the cluster if isinstance(p.value, dict): for symbol in list(p.value.keys()): if symbol not in np.unique(cluster.get_chemical_symbols()): LOGGER.warning('You provided a maximum tl value for ' '\'%s\' atoms, but there is no such ' 'chemical symbol in your cluster.', symbol) class SourceParameters(BaseParameters): def __init__(self, global_parameters=None, phagen_parameters=None, spec_parameters=None): parameters = ( Parameter('energy', types=(list, tuple, int, float), limits=(0, None), unit=UREG.eV, doc=textwrap.dedent(""" The photon energy in eV. Common laboratories X-ray source Mg |kalpha| and Al |kalpha| lines are defined in the :py:class:`msspec.misc.XRaySource` class. For example: .. highlight:: python :: >>> from msspec.calculator import MSSPEC >>> calc = MSSPEC() >>> calc.source_parameters.energy = XRaySource.MG_KALPHA >>> print calc.source_parameters.energy 1253.6 """), default=XRaySource.MG_KALPHA), Parameter('theta', types=(int, float), limits=(-180., 180.), unit=UREG.degree, default=-55., doc=textwrap.dedent(""" The polar angle of the photon incidence (in degrees). Please refer to :ref:`this figure ` for questions regarding the proper orientation. """)), Parameter('phi', types=(int, float), limits=(0., 360.), unit=UREG.degree, default=0., doc=textwrap.dedent(""" The azimuthal angle of the photon incidence (in degrees). Please refer to :ref:`this figure ` for questions regarding the proper orientation. """)), ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.global_parameters = global_parameters self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters self.freeze() def bind_energy(self, p): spectro = self.global_parameters.spectroscopy if spectro in ('PED',): assert isinstance(p.value, (int, float)), ( 'Only a single value for the light is allowed in ' 'PhotoElectron Diffraction spectroscopy') self.spec_parameters.ped_elum = float(p.value) LOGGER.info('Incomming photon energy set to %s', p.value * p.unit) else: LOGGER.warning('Setting the source energy is pointless in %s ' 'spectroscopy. Statement ignored.', spectro) def bind_theta(self, p): spectro = self.global_parameters.spectroscopy if spectro in ('PED', 'EXAFS', 'LEED'): self.spec_parameters.ped_thlum = float(p.value) self.spec_parameters.leed_thini = float(p.value) self.spec_parameters.exafs_thlum = float(p.value) LOGGER.info('Incomming photon beam theta angle set to %s', p.value * p.unit) else: LOGGER.warning('Setting the source beam polar (theta) angle is ' 'pointless in %s spectroscopy. Statement ignored.', spectro) def bind_phi(self, p): spectro = self.global_parameters.spectroscopy if spectro in ('PED', 'EXAFS', 'LEED'): self.spec_parameters.ped_philum = float(p.value) self.spec_parameters.leed_phiini = float(p.value) self.spec_parameters.exafs_philum = float(p.value) LOGGER.info('Incomming photon beam theta angle set to %s', p.value * p.unit) else: LOGGER.warning('Setting the source beam azimutal (phi) angle is ' 'pointless in %s spectroscopy. Statement ignored.', spectro) class DetectorParameters(BaseParameters): def __init__(self, global_parameters, phagen_parameters, spec_parameters): parameters = ( Parameter('angular_acceptance', types=(int, float), unit=UREG.degree, limits=(0., None), default=0., doc=textwrap.dedent(""" The angular acceptance of the detector in degrees used when the *average_sampling* option is enabled below. """)), Parameter('average_sampling', types=(type(None), str), allowed_values=(None, 'low', 'medium', 'high'), default=None, doc=textwrap.dedent(""" Used to averaged the signal over directions lying in the cone of half-angle *angular_acceptance*. The number of directions to take into account depends on the choosen value: - **None**, for no averaging at all - '**low**', to average over 5 directions - '**medium**', to average over 13 directions - '**high**', to average over 49 directions """)), Parameter('rotate', types=bool, default=False, doc=textwrap.dedent(""" When False, the sample is rotated when doing a scan (the usual way). Otherwise, the detector is rotated. """)) ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.global_parameters = global_parameters self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters self.freeze() def bind_angular_acceptance(self, p): spectro = self.global_parameters.spectroscopy if spectro in ('PED', 'LEED', 'AED'): self.spec_parameters.ped_accept = float(p.value) self.spec_parameters.leed_accept = float(p.value) self.spec_parameters.aed_accept = float(p.value) LOGGER.info('Detector angular acceptance set to %s', p.value * p.unit) else: LOGGER.warning('Setting the detector angular acceptance is ignored ' 'in %s spectroscopy.', spectro) def bind_average_sampling(self, p): spectro = self.global_parameters.spectroscopy if spectro in ('PED', 'LEED', 'AED'): imoy = p.allowed_values.index(p.value) self.spec_parameters.ped_imoy = imoy self.spec_parameters.leed_imoy = imoy self.spec_parameters.aed_imoy = imoy LOGGER.info('Detector average sampling set to %s', p.value.upper()) else: LOGGER.warning('Setting the average sampling is ignored in %s ' 'spectroscopy.', spectro) def bind_rotate(self, p): spectro = self.global_parameters.spectroscopy if spectro in ('PED', 'LEED', 'AED'): imod = int(not p.value) self.spec_parameters.ped_imod = imod self.spec_parameters.leed_imod = imod self.spec_parameters.aed_imod = imod if imod == 0: LOGGER.info('The DETECTOR will rotate') else: LOGGER.info('The SAMPLE will rotate') else: LOGGER.warning('Setting the detector ratation is ignored in %s ' 'spectroscopy.', spectro) class ScanParameters(BaseParameters): def __init__(self, global_parameters, phagen_parameters, spec_parameters): parameters = ( Parameter('type', allowed_values=('theta', 'phi', 'theta_phi', 'energy', 'scatf'), types=str, default='theta'), Parameter('theta', types=(np.ndarray, list, tuple, int, float), unit=UREG.degree, limits=(-90., 90.), default=np.linspace(0., 88., 45)), Parameter('phi', types=(np.ndarray, list, tuple, int, float), unit=UREG.degree, limits=(0., 360.), default=np.array([0.])), Parameter('kinetic_energy', types=(list, tuple, int, float), unit=UREG.eV, limits=(0., None), default=200., doc=textwrap.dedent(""" if given as a list or tuple: * with 2 elements, 10 points of energy will be generated with the first element as the starting energy and the second element as the stopping energy. * with 3 elements, the first element is the starting energy the second one is the stopping energy and the last one is the number of points. if given as a float or integer, there will be one point for the kinetic energy. """)), Parameter('ke_array', types=np.ndarray, unit=UREG.eV, default=np.array([200., ]), private=True) ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters self.global_parameters = global_parameters self.spec_parameters.extra_parameters['scan'] = self self.freeze() def bind_type(self, p): spectro = self.global_parameters.spectroscopy calculation_parameters = self.spec_parameters.extra_parameters[ 'calculation'] scantype = p.value if spectro in ('EXAFS',): LOGGER.warning('No scan type are allowed in %s spectroscopy. This ' 'will be ignored.', spectro) return if spectro in ('AED',) and scantype == 'energy': LOGGER.error('Energy scan is not compatible with %s ' 'spectroscopy.', spectro) raise ValueError params = { 'phi': (2, 0, 0, 0), 'theta': (0, 2, 0, 0), 'energy': (0, 0, 1, 0), 'scatf': (0, 1, 0, 1), 'theta_phi': (-1, 0, 0, 0)} self.spec_parameters.ped_iphi = params[scantype][0] self.spec_parameters.ped_itheta = params[scantype][1] self.spec_parameters.ped_ie = params[scantype][2] self.spec_parameters.ped_ifthet = params[scantype][3] self.spec_parameters.leed_iphi = params[scantype][0] self.spec_parameters.leed_itheta = params[scantype][1] self.spec_parameters.leed_ie = params[scantype][2] self.spec_parameters.leed_ifthet = params[scantype][3] self.spec_parameters.aed_iphi = params[scantype][0] self.spec_parameters.aed_itheta = params[scantype][1] self.spec_parameters.aed_ifthet = params[scantype][3] if scantype == 'scatf': self.spec_parameters.ped_theta0 = -360. self.spec_parameters.ped_theta1 = 0. self.spec_parameters.ped_ntheta = 577 self.spec_parameters.ped_nfthet = 577 self.spec_parameters.leed_theta0 = -360. self.spec_parameters.leed_theta1 = 0. self.spec_parameters.leed_ntheta = 577 self.spec_parameters.leed_nfthet = 577 self.spec_parameters.aed_theta0 = -360. self.spec_parameters.aed_theta1 = 0. self.spec_parameters.aed_ntheta = 577 self.spec_parameters.aed_nfthet = 577 if scantype == "scatf": calculation_parameters.set_parameter('basis_functions', 'plane_wave', force=True) else: calculation_parameters.set_parameter('basis_functions', 'spherical', force=True) LOGGER.info('\'%s\' scan type choosen.', p.value) def bind_theta(self, p): spectro = self.global_parameters.spectroscopy scantype = self.get_parameter('type').value if scantype == 'scatf': comment = 'with the scattering factor scan type.' if spectro == 'EXAFS': comment = 'in EXAFS spetroscopy.' if spectro in ('EXAFS',) or scantype in ('scatf',): msg = 'Setting the theta angle is not possible %s' % comment LOGGER.error('Incompatible options!') raise ValueError(msg) # p._value = np.array(p.value, dtype=np.float).flatten() arr = np.array(p.value, dtype=np.float).flatten() theta0 = arr[0] theta1 = arr[-1] ntheta = len(arr) self.spec_parameters.ped_theta0 = theta0 self.spec_parameters.ped_theta1 = theta1 self.spec_parameters.ped_ntheta = ntheta self.spec_parameters.leed_theta0 = theta0 self.spec_parameters.leed_theta1 = theta1 self.spec_parameters.leed_ntheta = ntheta self.spec_parameters.aed_theta0 = theta0 self.spec_parameters.aed_theta1 = theta1 self.spec_parameters.aed_ntheta = ntheta LOGGER.info('theta = %s (%d points)', arr * p.unit, len(arr)) return arr def bind_phi(self, p): spectro = self.global_parameters.spectroscopy scantype = self.get_parameter('type').value if scantype == 'scatf': comment = 'with scattering factor scan type.' if spectro == 'EXAFS': comment = 'in EXAFS spetroscopy.' if spectro in ('EXAFS',) or scantype in ('scatf',): msg = 'Setting the phi angle is not possible %s' % comment LOGGER.error('Incompatible options') raise ValueError(msg) arr = np.array(p.value, dtype=np.float).flatten() phi0 = arr[0] phi1 = arr[-1] nphi = len(arr) self.spec_parameters.ped_phi0 = phi0 self.spec_parameters.ped_phi1 = phi1 self.spec_parameters.ped_nphi = nphi self.spec_parameters.leed_phi0 = phi0 self.spec_parameters.leed_phi1 = phi1 self.spec_parameters.leed_nphi = nphi self.spec_parameters.aed_phi0 = phi0 self.spec_parameters.aed_phi1 = phi1 self.spec_parameters.aed_nphi = nphi LOGGER.info('phi = %s (%d points)', arr * p.unit, len(arr)) return arr def bind_kinetic_energy(self, p): npts = 10 energies = [0, 0, npts] if isinstance(p.value, (list, tuple, np.ndarray)): assert len(p.value) <= 3, ( 'kinetic_energy -- if given as a list -- must have at most ' '3 elements, in which case:\n' '* the first element is the starting energy,\n' '* the second one is the stopping energy,\n' '* and the last one is the number of points.\n' 'If the last element is omitted, %s points are ' 'assumed' % str(npts)) energies[0] = float(p.value[0]) energies[1] = float(p.value[0]) if len(p.value) > 1: energies[1] = float(p.value[1]) if len(p.value) > 2: energies[-1] = int(p.value[-1]) else: energies[0] = float(p.value) energies[1] = float(p.value) energies[2] = 1 ke_array_eV = np.linspace(*energies) ke_array_ry = (ke_array_eV * p.unit).to('rydberg') emin = np.min(ke_array_ry) emax = np.max(ke_array_ry) npts = len(ke_array_ry) assert npts > 0, 'The number of energy points must be >= 1' if npts == 1: delta = 0.3 * UREG.rydberg else: delta = (emax - emin) / (npts - 1) self.phagen_parameters.emin = emin.magnitude self.phagen_parameters.emax = emax.magnitude self.phagen_parameters.delta = float(delta.magnitude) self.spec_parameters.ped_e0 = energies[0] self.spec_parameters.ped_e1 = energies[1] self.spec_parameters.ped_ne = energies[2] self.spec_parameters.eigval_ekini = energies[0] self.spec_parameters.eigval_ekfin = energies[1] self.spec_parameters.eigval_ne = energies[2] self.set_parameter('ke_array', ke_array_eV, force=True) LOGGER.info('Kinetic energy = %s (%d points)', ke_array_eV, npts) class CalculationParameters(BaseParameters): def __init__(self, global_parameters, phagen_parameters, spec_parameters): parameters = ( Parameter('RA_cutoff', types=int, limits=(0, 8), default=1, doc=textwrap.dedent(""" The Rehr-Albers cut-off parameter which controls the degree of sphericity introduced in the description of the basis functions used to expand the wave function around each atomic center. It is only meaningful for the series expansion algorithm. Its value is limited to 8 but it is rarely necessary to go beyond 2 or 3.""")), Parameter('scattering_order', types=int, limits=(1, 10), default=3, doc=textwrap.dedent(""" The scattering order. Only meaningful for the 'expansion' algorithm. Its value is limited to 10.""")), Parameter('RA_cutoff_damping', types=int, limits=(0, None), default=0, doc=textwrap.dedent(""" The Rehr-Albers truncation order. If > 0, the *RA_cutoff* is decreased by 1 every *i*\ :sup:`th` scatterer until 0, where *i* = *RA_cutoff_damping*.""")), Parameter('spin_flip', types=bool, default=False, doc=textwrap.dedent(""" This parameter tells if spin-flip is authorized or not in the scattering process. :Note: This option works only if the spin polarization is enabled in your calculator object (see spinpol_).""")), Parameter('integrals', types=str, allowed_values=('all', 'diagonal'), default='all', doc=textwrap.dedent(""" This option allows to take into account all four radial integrals (R++, R+-, R-+ and R--) in the calculation or only the diagonal radial integrals (R++ and R--) which are generally much larger. .. note:: This option works only if the spin polarization is enabled in your calculator object. """)), Parameter('path_filtering', types=(type(None), str, tuple, list), allowed_values=(None, 'forward_scattering', 'backward_scattering', 'distance_cutoff', 'plane_wave_normal', 'plane_wave_spin_averaged'), default=None, doc=textwrap.dedent(""" Used to activate some filters. It is possible to specify several of them by grouping them in a tuple or a list. For example:: >>> my_filters = ('forward_scattering', 'backward_scattering') >>> calc.calculation_parameters.path_filtering = my_filters """)), Parameter('off_cone_events', types=int, limits=(0, None), default=1, doc=textwrap.dedent(""" Used in conjunction with the '*forward_scattering*' filter. If the number of scattering processes outside the forward (or backward) scattering cone is greater than this number, then the path is rejected and its contribution to the scattering path operator won’t be computed. """)), Parameter('scattering_order_cutoff', types=int, limits=(0, 10), default=2, doc=textwrap.dedent(""" Used in conjunction with the ‘*plane_wave_normal*’ filter. It states to activate the plane wave approximation (which is fast but less accurate) to compute the contribution when the scattering order is greater than this value.""")), Parameter('distance', types=(int, float), limits=(0, None), unit=UREG.angstroms, default=10., doc=textwrap.dedent(""" Used with the '*distance_cut_off*' filter. Paths whose length is larger than this value are simply rejected.""")), Parameter('vibrational_damping', types=(type(None), str), allowed_values=(None, 'debye_waller', 'averaged_tl'), default='debye_waller', doc=textwrap.dedent(""" Tells how to compute the effect of atomic vibrations. It can be: - '**debye_waller**' for using the Debye Waller model. - '**averaged_tl**' to use the more correct averaging over T-matrix elements.""")), Parameter('temperature', types=(int, float), limits=(0, None), unit=UREG.degK, default=293., doc=textwrap.dedent(""" The temperature of the cluster. Used when *use_debye_model* = True """)), Parameter('debye_temperature', types=(int, float), limits=(0, None), unit=UREG.degK, default=420., doc=textwrap.dedent(""" The Debye temperature used for the calculation of the mean square displacements if *use_debye_model* = True""")), Parameter('use_debye_model', types=bool, default=False, doc=textwrap.dedent(""" No matter the way you compute the effect of atomic vibrations, you need the mean square displacements of atoms. It can be computed internally following the Debye model if you set this option to True. """)), Parameter('vibration_scaling', types=(int, float), limits=(0., None), default=1.2, doc=textwrap.dedent(""" Used to simulate the fact that surface atoms vibrate more than bulk ones. It is a factor applied to the mean square displacements. """)), Parameter('basis_functions', types=str, allowed_values=( 'plane_wave', 'spherical'), default='spherical', private=True), Parameter('cutoff_factor', types=(int, float), limits=(1e-4, 999.9999), default=0.01, private=True), Parameter('mean_free_path', types=(int, float, str), default='SeahDench', allowed_values=('mono', 'SeahDench'), doc=textwrap.dedent(""" The electron mean free path value. You can either: - Enter a value (in Angströms), in this case any value <=0 will disable the damping - Enter the keyword 'mono' to use a formula valid only for monoelemental samples - Enter the keyword 'SeahDench' to use the Seah and Dench formula. .. note:: The mean free path is only taken into account when the input T-matrix corresponds to a real potential as, when the potential is complex, this damping is taken care of by the imaginery part othe potential. """)), ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.global_parameters = global_parameters self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters self.spec_parameters.extra_parameters['calculation'] = self self.freeze() def bind_RA_cutoff(self, p): self.spec_parameters.calc_no = p.value LOGGER.info('Rehr-Albers cutoff parameter set to %s', p.value) def bind_scattering_order(self, p): self.spec_parameters.calc_ndif = p.value LOGGER.info('Scattering order set to %s', p.value) def bind_RA_cutoff_damping(self, p): self.spec_parameters.calc_ino = p.value LOGGER.info('Rehr-Albers cutoff damping set to %s', p.value) def bind_basis_functions(self, p): if p.value == 'plane_wave': ispher = 0 elif p.value == 'spherical': ispher = 1 self.spec_parameters.calc_ispher = ispher LOGGER.info('Type of basis functions: \'%s\'', p.value) def bind_spin_flip(self, p): if self.global_parameters.spinpol is False: err_msg = ( "'{}' is ignored since the 'spinpol' global parameter is set " "to False. Enable spin polarization in the constructor of " "your Calculator if you want to use this option." ).format(p.name) LOGGER.error("Incompatible options!") raise ValueError(err_msg) isflip = int(p.value) self.spec_parameters.calc_isflip = isflip LOGGER.info('Spin-flip set to %s', p.value) def bind_integrals(self, p): if self.global_parameters.spinpol is False: err_msg = ( "'{}' is ignored since the 'spinpol' global parameter is set " "to False. Enable spin polarization in the constructor of " "your Calculator if you want to use this option." ).format(p.name) LOGGER.error("Incompatible options!") raise ValueError(err_msg) irdia = 0 if p.value == 'all' else 1 self.spec_parameters.calc_irdia = irdia LOGGER.info('Radial integrals taken into account: %s', p.value) def bind_path_filtering(self, p): ifwd = ipw = ilength = 0 ipp = 1 if ('plane_wave_spin_averaged' in p.value and 'plane_wave_normal' in p.value): err_msg = ( "Only one plane wave filter (either 'plane_wave_normal' or " "'plane_wave_spin_averaged') can be used at a time (along " "with other filters if needed).") LOGGER.error('Incompatible options!') raise ValueError(err_msg) if p.value != None: if 'forward_scattering' in p.value: ifwd = 1 if 'backward_scattering' in p.value: ifwd = 1 if 'distance_cutoff' in p.value: ilength = 1 if 'plane_wave_normal' in p.value: ipw = 1 ipp = 1 if 'plane_wave_spin_averaged' in p.value: ipw = 1 ipp = 2 self.spec_parameters.calc_ifwd = ifwd self.spec_parameters.calc_ipw = ipw self.spec_parameters.calc_ilength = ilength self.spec_parameters.calc_ipp = ipp LOGGER.info('Filters activated: %s', p.value) def bind_off_cone_events(self, p): self.spec_parameters.calc_nthout = p.value LOGGER.info('Off cone events set to %s', p.value) path_filtering = self.get_parameter('path_filtering').value f = 'forward_scattering' if path_filtering is not None and f not in path_filtering: LOGGER.warning("'%s' option set but ignored since the " "'%s' filter' is not activated", p.name, f) def bind_scattering_order_cutoff(self, p): self.spec_parameters.calc_ncut = p.value LOGGER.info('Scattering order cutoff set to %s', p.value) path_filtering = self.get_parameter('path_filtering').value f = 'plane_wave_normal' if path_filtering is not None and f not in path_filtering: LOGGER.warning("'%s' option set but ignored since the " "'%s' filter' is not activated", p.name, f) def bind_cutoff_factor(self, p): self.spec_parameters.calc_pctint = float(p.value) LOGGER.info('Cutoff factor set tp %s', p.value) def bind_distance(self, p): self.spec_parameters.calc_rlength = float(p.value) LOGGER.info('Distance cutoff set to %s', p.value * p.unit) path_filtering = self.get_parameter('path_filtering').value f = 'distance_cutoff' if path_filtering is not None and f not in path_filtering: LOGGER.warning("'%s' option set but ignored since the " "'%s' filter' is not activated", p.name, f) def bind_vibrational_damping(self, p): #self.spec_parameters.calc_ispeed = 1 if p.value is None: LOGGER.info('Vibrational damping disabled') # no spec parameters updated here since it is done # when writing the data input file by setting the # sample temperature to 0K and using the debye_model return if p.value == 'debye_waller': self.spec_parameters.calc_idwsph = 0 elif p.value == 'averaged_tl': self.spec_parameters.calc_idwsph = 1 LOGGER.info('Vibrational damping activated with \'%s\' model', p.value) def bind_temperature(self, p): self.spec_parameters.calc_t = float(p.value) LOGGER.info('Sample temperature set to %s', p.value * p.unit) if not self.get_parameter('use_debye_model').value: LOGGER.warning("The sample temperature was set, but will be " "ignored since 'use_debye_model' parameter is " "False.") def bind_debye_temperature(self, p): self.spec_parameters.calc_td = float(p.value) LOGGER.info('Sample Debye temperature set to %s', p.value * p.unit) if not self.get_parameter('use_debye_model').value: LOGGER.warning("The sample Debye temperature was set, but will be " "ignored since 'use_debye_model' parameter is " "False.") def bind_use_debye_model(self, p): if p.value: self.spec_parameters.calc_idcm = 1 else: self.spec_parameters.calc_idcm = 0 LOGGER.info('use of the Debye model for mean square displacements: %s', p.value) if self.get_parameter('vibrational_damping').value is None: LOGGER.warning("'use_debye_model' parameter was set but will be " "ignored as long as vibrational_damping is " "disbaled.") def bind_vibration_scaling(self, p): self.spec_parameters.calc_rsj = float(p.value) LOGGER.info('Vibration scaling set to: %s', p.value) def bind_mean_free_path(self, p): if isinstance(p.value, str): if p.value == 'mono': self.spec_parameters.calc_ilpm = 1 elif p.value == 'SeahDench': self.spec_parameters.calc_ilpm = 2 else: if p.value <= 0: self.spec_parameters.calc_ilpm = -1 else: self.spec_parameters.calc_ilpm = 0 self.spec_parameters.calc_xlpm0 = float(p.value) LOGGER.info('Mean free path set to: %s', str(p.value)) class PEDParameters(BaseParameters): def __init__(self, phagen_parameters, spec_parameters): parameters = ( Parameter('level', types=str, pattern=r'\d+[spdfgSPDFG](\d/2)?$', default='1s', doc=textwrap.dedent(""" The level is the electronic level where the electron comes from. It is written: *nlJ* where: - *n* is the principal quantum number - *l* is the orbital quantum number - *J* is the spin-orbit component Example:: >>> calc.spectroscopy_parameters.level = '2p3/2' >>> calc.spectroscopy_parameters.level = '2p' # is equivalent to '2p1/2' """)), Parameter('final_state', types=int, limits=(-1, 2), default=2), Parameter('spin_orbit', types=(type(None), str), allowed_values=(None, 'single', 'both'), default=None), ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters def bind_level(self, p): edge = get_level_from_electron_configuration(p.value) self.phagen_parameters.edge = edge li, so = re.match(r'(^\d+[spdfg])(.*$)', p.value).groups() if so == '': so = '1/2' self.spec_parameters.ped_li = li self.spec_parameters.ped_so = so self.spec_parameters.extra_level = p.value def bind_final_state(self, p): self.spec_parameters.ped_initl = p.value def bind_spin_orbit(self, p): somap = { None: 0, 'single': 1, 'both': 2} self.spec_parameters.ped_so = somap[p.value] class EIGParameters(BaseParameters): def __init__(self, phagen_parameters, spec_parameters): parameters = ( Parameter('level', types=str, pattern=r'\d+[spdfgSPDFG](\d/2)?$', default='1s', doc=textwrap.dedent(""" The level is the electronic level where the electron comes from. It is written: *nlJ* where: - *n* is the principal quantum number - *l* is the orbital quantum number - *J* is the spin-orbit component Example:: >>> calc.spectroscopy_parameters.level = '2p3/2' >>> calc.spectroscopy_parameters.level = '2p' # is equivalent to '2p1/2' """)), Parameter('final_state', types=int, limits=(-1, 2), default=2), Parameter('spin_orbit', types=(type(None), str), allowed_values=(None, 'single', 'both'), default=None), Parameter('kernel_matrix_spectrum', types=(bool,), default=False, doc=textwrap.dedent(""" Whether to output the kernel matrix spectrum for each energy point. """)), ) BaseParameters.__init__(self) self.add_parameters(*parameters) self.phagen_parameters = phagen_parameters self.spec_parameters = spec_parameters def bind_level(self, p): edge = get_level_from_electron_configuration(p.value) self.phagen_parameters.edge = edge li, so = re.match(r'(^\d+[spdfg])(.*$)', p.value).groups() if so == '': so = '1/2' self.spec_parameters.ped_li = li self.spec_parameters.ped_so = so self.spec_parameters.extra_level = p.value