Source code for Inelastica.misc.valuecheck

"""

:mod:`Inelastica.misc.valuecheck`
=================================

Contains general quantities that are used to do checks in the
`Inelastica`_ package.

It allows users to edit the criterias of crashes without editing
the code.

Currently if this is to be used with `Inelastica`_ or `pyTBT`_, it should
be in a wrapper call which emulates the options.

.. currentmodule:: Inelastica.misc.valuecheck

"""
from __future__ import print_function

import sys as s
import numpy as _np


# Python 3-version
# Check of strings
if s.version_info[0] == 3:
    string_types = str,
else:
    string_types = str,

# Place holder for checks
_check = {}


def _s(name, value): _check[name] = value

# The lower magnitude of the Hamiltonian/overlap elements between
# the device region and electrode
_s("Device-Elec-overlap", 1e-7)

# The tolerance of difference in onlyS and FC displacements
_s("displacement-tolerance", 0.05)

# The tolerance for which the k-points are the same
_s("same-kpoint", 1e-9)

# The tolerance when ensuring that the imaginary part is zero
_s("zero-imaginary-part", 1e-8)
_s("trans-imaginary-part", 1e-10)

# Convergence criteria for Lopez-Sancho algorithm
_s("Lopez-Sancho", 1e-5)
_s("Lopez-Sancho-warning", 1e-8)

del _s


[docs] def GetCheck(name): global _check return _check[name]
[docs] def EditCheck(name, value): """ Sets the check of "name" to the value "value". It will notify the user so that any log files will retain this information. """ global _check ov = "None" if name in _check: ov = _check[name] _check[name] = value print("WARNING: Overriding variable '{0}'".format(name)) print(" Old value: {0}".format(ov)) print(" New value: {0}".format(value))
[docs] def Check(name, val, *msgs): """ Checks the value and exits if the value "val" is above the one specified """ global _check if not name in _check: # the name hasn't been set # hence, it will always go through # This allows the coder to insets fully user tests # Say if the code should generally never stop, we can make # some checks where users might have certain criterias. return if _np.any(_np.array(val) > _check[name]): print("ERROR:") for msg in msgs: print(msg) print(" Check against '{0}' failed...".format(name)) print(" Maximum value: {0}".format(_np.max(val))) print(" Error value : {0}".format(_check[name])) raise ArithmeticError("Criteria not met. Please check output...")
[docs] def OptionsCheck(opts): """ Generic routine for adjusting most used options for routines. I.e. Inelastica/EigenChannels/pyTBT. """ import Inelastica.io.siesta as SIO import os import os.path as osp # Module name exe = opts.module # Destination directory if not osp.isdir(opts.DestDir): print('\n'+exe+': Creating folder {0}'.format(opts.DestDir)) os.mkdir(opts.DestDir) if not osp.isfile(opts.fn): raise IOError("FDF-file not found: "+opts.fn) # Read SIESTA files opts.head = osp.split(opts.fn)[0] if opts.head == '': # set filepath if missing opts.head = '.' print(exe+": Reading keywords from {0} \n".format(opts.fn)) opts.systemlabel = SIO.GetFDFlineWithDefault(opts.fn, 'SystemLabel', str, 'siesta', exe) opts.TSHS = '%s/%s.TSHS'%(opts.head, opts.systemlabel) # These first keys can be used, but they are superseeded by keys in the TS.Elec.<> block # Hence if they are read in first it will do it in correct order. if opts.UseBulk < 0: # Note NRP: # in principle this is now a per-electrode setting which # may be useful for certain systems... opts.UseBulk = SIO.GetFDFlineWithDefault(opts.fn, 'TS.UseBulkInElectrodes', bool, True, exe) opts.UseBulk = SIO.GetFDFlineWithDefault(opts.fn, 'TS.Elecs.Bulk', bool, opts.UseBulk, exe) def get_elec_vars(lr): # Look up old format first TSHS = SIO.GetFDFlineWithDefault(opts.fn, 'TS.HSFile'+lr, str, '', exe) NA1 = SIO.GetFDFlineWithDefault(opts.fn, 'TS.ReplicateA1'+lr, int, 1, exe) NA2 = SIO.GetFDFlineWithDefault(opts.fn, 'TS.ReplicateA2'+lr, int, 1, exe) # default semi-inf direction semiinf = 2 # Proceed looking up new format, which precedes belec = 'TS.Elec.' + lr print('Looking for new electrode format in: %%block {}'.format(belec)) # Default replication stuff TSHS = SIO.GetFDFlineWithDefault(opts.fn, belec+'.TSHS', str, TSHS, exe) NA1 = SIO.GetFDFlineWithDefault(opts.fn, belec+'.Bloch.A1', int, NA1, exe) NA2 = SIO.GetFDFlineWithDefault(opts.fn, belec+'.Bloch.A2', int, NA2, exe) NA3 = SIO.GetFDFlineWithDefault(opts.fn, belec+'.Bloch.A3', int, 1, exe) # Overwrite block block = SIO.GetFDFblock(opts.fn, KeyWord=belec) for line in block: print(line) # Lower-case, FDF is case-insensitive key = line[0].lower() if key in ['tshs', 'tshs-file', 'hs', 'hs-file']: TSHS = line[1] elif key in ['replicate-a', 'rep-a', 'replicate-a1', 'rep-a1', 'bloch-a1']: NA1 = int(line[1]) elif key in ['replicate-b', 'rep-b', 'replicate-a2', 'rep-a2', 'bloch-a2']: NA2 = int(line[1]) elif key in ['replicate-c', 'rep-c', 'replicate-a3', 'rep-a3', 'bloch-a3']: NA3 = int(line[1]) elif key in ['replicate', 'rep', 'bloch']: # We have *at least* 2 integers NA1 = int(line[1]) NA2 = int(line[2]) NA3 = int(line[3]) elif key in ['semi-inf-direction', 'semi-inf-dir', 'semi-inf']: # This is lower-case checked axis = line[1][1:].lower() if 'a' == axis or 'a1' == axis: semiinf = 0 elif 'b' == axis or 'a2' == axis: semiinf = 1 elif 'c' == axis or 'a3' == axis: semiinf = 2 # Simple check of input, this may be overwritten later if semiinf != 2 and NA1 * NA2 * NA3 > 1: raise ValueError(("Cannot provide semi-infinite directions " "other than A3-direction with repetition.")) if TSHS[0] != '/': # path is relative TSHS = opts.head+'/'+TSHS return TSHS, NA1, NA2, semiinf # Look up electrode block block = SIO.GetFDFblock(opts.fn, KeyWord='TS.Elecs') if len(block) == 0: # Did not find the electrode block, defaults to old naming scheme opts.fnL, opts.NA1L, opts.NA2L, opts.semiinfL = get_elec_vars('Left') opts.fnR, opts.NA1R, opts.NA2R, opts.semiinfR = get_elec_vars('Right') elif len(block) == 2: # NB: The following assumes that the left electrode is the first in the block! opts.fnL, opts.NA1L, opts.NA2L, opts.semiinfL = get_elec_vars(block[0][0]) opts.fnR, opts.NA1R, opts.NA2R, opts.semiinfR = get_elec_vars(block[1][0]) else: print(block) raise IOError('Currently only two electrodes are supported') # Read in number of buffer atoms opts.buffer, L, R = SIO.GetBufferAtomsList(opts.TSHS, opts.fn) opts.bufferL = L opts.bufferR = R if 'maxBias' in opts.__dict__: # Bias range opts.maxBias = abs(opts.maxBias) opts.minBias = -abs(opts.maxBias) # Device region if opts.DeviceFirst <= 0: opts.DeviceFirst = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.PDOSFrom', int, 1, exe) opts.DeviceFirst -= L if opts.DeviceLast <= 0: opts.DeviceLast = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.PDOSTo', int, 1e10, exe) opts.DeviceLast -= L opts.NumberOfAtoms = SIO.GetFDFlineWithDefault(opts.fn, 'NumberOfAtoms', int, 1e10, exe) opts.NumberOfAtoms -= L + R if opts.DeviceLast < opts.DeviceFirst: print(exe+' error: DeviceLast<DeviceFirst not allowed. Setting DeviceLast=DeviceFirst') opts.DeviceLast = opts.DeviceFirst opts.DeviceAtoms = [max(opts.DeviceFirst, 1), min(opts.DeviceLast, opts.NumberOfAtoms)] # Voltage opts.voltage = SIO.GetFDFlineWithDefault(opts.fn, 'TS.Voltage', float, 0.0, exe) ############# # Here comes some specifics related to different executables: ############# if "VfracL" in opts.__dict__: if opts.VfracL < 0.0 or opts.VfracL > 1.0: raise RuntimeError('Option VfracL must be a value in the range [0,1].') if "PhononNetCDF" in opts.__dict__: if not osp.isfile(opts.PhononNetCDF): raise IOError("Electron-phonon coupling NetCDF file not found: "+opts.PhononNetCDF) if "eta" in opts.__dict__: if opts.eta < 0: raise RuntimeError("eta must be a posivite number") if "etaLead" in opts.__dict__: if opts.etaLead < 0: raise RuntimeError("etaLead must be a posivite number") if "PhExtDamp" in opts.__dict__: if opts.PhExtDamp < 0: raise RuntimeError("PhExtDamp must be a posivite number") if "biasPoints" in opts.__dict__: if opts.biasPoints < 6: raise AssertionError("BiasPoints must be larger than 5") if "iSpin" in opts.__dict__: if not opts.iSpin in [0, 1]: raise AssertionError("Spin must be either 0 or 1") if "Emin" in opts.__dict__: if opts.Emin == 1e10: opts.Emin = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.Emin', float, 0.0, 'pyTBT') if "Emax" in opts.__dict__: if opts.Emax == 1e10: opts.Emax = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.Emax', float, 1.0, 'pyTBT') if "NPoints" in opts.__dict__: if opts.NPoints <= 0: opts.NPoints = SIO.GetFDFlineWithDefault(opts.fn, 'TS.TBT.NPoints', int, 1, 'pyTBT') # Create list of energies try: opts.Elist # Do not overwrite if some Elist is already specified except: if opts.NPoints == 1: opts.Elist = _np.array((opts.Emin,), _np.float) else: # Linspace is just what we need opts.Elist = _np.linspace(opts.Emin, opts.Emax, opts.NPoints)
[docs] def GetPositional(args, msg="You have not specified any positional argument"): if len(args) < 1: raise ValueError(msg) pos = args.pop(0) return pos