"""
Units from various programs
This conversion tool is inspired by the SIESTA fdf-parser in its
group-construct.
"""
__all__ = ['unit_group', 'unit_convert', 'unit_default']
# We do not import anything as it depends on the package.
# Here we only add the conversions according to the
# standard. Other programs may use their units as they
# please with non-standard conversion factors.
_unit_table = {
'mass': {
'DEFAULT': 'amu',
'kg': 1.,
'g': 1.e-3,
'amu': 1.66054e-27,
},
'length': {
'DEFAULT': 'Ang',
'm': 1.,
'cm': 0.01,
'nm': 1.e-9,
'Ang': 1.e-10,
'Bohr': 5.29177249e-11,
},
'time': {
'DEFAULT': 'fs',
's': 1.,
'fs': 1.e-15,
'ps': 1.e-12,
'ns': 1.e-9,
},
'energy': {
'DEFAULT': 'eV',
'J': 1.,
'erg': 1.e-7,
'eV': 1.60217733e-19,
'meV': 1.60217733e-16,
'Ry': 2.1798741e-18,
'Ha': 4.3597482e-18,
'Hartree': 4.3597482e-18,
'K': 1.380648780669e-23,
},
'force': {
'DEFAULT': 'eV/Ang',
'N': 1.,
'eV/Ang': 1.60217733e-9,
}
}
[docs]def unit_group(unit, tbl=None):
"""
Returns the unit group that is associated with
input unit.
Parameters
----------
unit : str
unit, e.g. kg, Ang, eV etc. returns the type of unit it is.
Examples
--------
>>> unit_group('kg')
'mass'
>>> unit_group('eV')
'energy'
"""
if tbl is None:
global _unit_table
tbl = _unit_table
for k in tbl:
if unit in tbl[k]:
return k
raise ValueError('The unit "'+str(unit)+'" could not be located in the table.')
[docs]def unit_default(group, tbl=None):
"""
Return the default unit in the group `group`.
Parameters
----------
group, str
look-up in the table for the default unit.
"""
if tbl is None:
global _unit_table
tbl = _unit_table
for k in tbl:
if group == k:
return tbl[k]['DEFAULT']
raise UnitError('The unit-group does not exist!')
[docs]def unit_convert(fr, to, opts={}, tbl=None):
"""
Returns the factor that takes 'fr' to the units of 'to'.
Parameters
----------
fr :
starting unit
to :
ending unit
opts :
controls whether the unit conversion is in powers or fractional units
Examples
-------
>>> unit_convert('kg','g')
1000
>>> unit_convert('eV','J')
1.60219e-19
"""
if tbl is None:
global _unit_table
tbl = _unit_table
# In the case that the conversion to is None, we should do nothing.
frU = 'FromNotFound'
frV = None
toU = 'ToNotFound'
toV = None
# Check that the unit types live in the same
# space
# TODO this currently does not handle if powers are taken into
# consideration.
for k in tbl:
if fr in tbl[k]:
frU = k
frV = tbl[k][fr]
if to in tbl[k]:
toU = k
toV = tbl[k][to]
if frU != toU:
raise ValueError('The unit conversion is not from the same group: '+frU+' to '+toU)
# Calculate conversion factor
val = frV / toV
for opt in ['^', 'power', 'p']:
if opt in opts:
val = val ** opts[opt]
for opt in ['*', 'factor', 'fac']:
if opt in opts:
val = val * opts[opt]
for opt in ['/', 'divide', 'div']:
if opt in opts:
val = val / opts[opt]
return val
# # These are older implementations which allows
# # numpy direct calculations (much like Unum package).
# # A single unit-object.
# # Contains functions to compare and convert a unit
# # to another unit.
# class Unit(object):
# """
# Container for the unit and the conversion factors etc.
# This will make it easier to maintain the units, and eventually change the
# usage.
# """
# def __new__(cls,*args,**kwargs):
# if isinstance(args[0],Unit):
# return args[0]
# #print('Creating new unit:',args)
# obj = object.__new__(cls)
# if len(args) == 1: # We are creating a unit without a variable name
# obj.variable = None
# obj.unit = args[0]
# else:
# obj.variable = args[0]
# # Typical case when passing a unit from another variable...
# if isinstance(args[1],Unit):
# obj.unit = args[1].unit
# else:
# obj.unit = args[1]
# # We need to handle some type of operator definitions
# # But how to handle them?
# for op in ['**','^','/','*']:
# pass
# return obj
# def type(self):
# """ Returns the type of unit this is, i.e. energy, length, time, etc. """
# for k,v in _ConversionTable.iteritems():
# if self.unit in v: return k
# def SI(self):
# """ Returns the SI conversion factor for the unit """
# for k,v in _ConversionTable.iteritems():
# if self.variable in v: return v[self.variable]
# def convert(self,to):
# """ Convert this unit to another and returns the conversion factor. """
# u = Unit(to)
# # This will raise an exception if the units are not of same type...
# conv = UnitConvert(self.unit,u.unit)
# #print('Converting:',self.variable,self.unit,u.unit)
# self.unit = deepcopy(u.unit)
# return conv
# def copy(self):
# """Method for copying the unit """
# return deepcopy(self)
# def __repr__(self):
# """ Return the unit in string format (XML type-like)"""
# return "<Unit variable='"+str(self.variable)+"' unit='"+str(self.unit)+"'/>"
# def __eq__(self,other):
# """ Returns true if the variable is the same as the other """
# return self.variable == other.variable
# def __copy__(self):
# return Unit(copy(self.variable),copy(self.unit))
# def __deepcopy__(self, memo):
# return Unit(deepcopy(self.variable),deepcopy(self.unit))
# class Units(object):
# """
# Container for many units.
# This will make it easier to maintain the units, and eventually change the
# usage.
# """
# def __new__(cls,*args):
# # Convert the tuple to a list...
# obj = object.__new__(cls)
# # The args are a list of Unit-objects, or a list of pairs which should be converted to a list of units.
# units = []
# i = 0
# while i < len(args):
# if isinstance(args[i],Unit):
# units.append(deepcopy(args[i]))
# else:
# assert i < len(args)-1, 'Can not grap a unit for: ' + str(args[i])
# units.append(deepcopy(Unit(args[i],args[i+1])))
# i += 1
# i += 1
# obj._units = units
# return obj
# def append(self,unit):
# """ Append a unit object """
# # We cannot have to similar units assigned...
# if isinstance(unit,Units):
# for au in unit:
# # Use the recursive routine (keep it simple)
# self.append(au)
# else:
# for u in self:
# if u == unit:
# raise Exception('Can not append a unit which already exists. Do not assign dublicate variables')
# self._units.append(deepcopy(unit))
# def update(self,unit):
# """ Updates unit object, adds it if it does not exist """
# if unit is None: return
# if isinstance(unit,Units):
# for u in unit:
# self.update(u)
# else:
# for u in self:
# if u.variable == unit.variable:
# u.unit = deepcopy(unit.unit)
# return
# self.append(unit)
# def unit(self,variable):
# """ Returns the unit object associated with the variable named variable"""
# # if it is none, return fast.
# if not variable: return None
# for i in self:
# if i.variable == variable:
# return i
# return None
# def copy(self):
# """ Copies this unit segment """
# return deepcopy(self)
# #################
# # General routines overwriting python models
# #################
# def __len__(self):
# return len(self._units)
# def __contains__(self,item):
# if isinstance(item,Unit):
# u = Unit(item.variable,None)
# else:
# u = Unit(item,None)
# for unit in self:
# if u.variable == unit.variable:
# return True
# return False
# def __repr__(self):
# """ Return the unit in string format (XML type-like)"""
# tmp = '<Units>'
# for unit in self:
# tmp += '\n ' + str(unit)
# tmp += '\n</Units>'
# return tmp
# def __iter__(self):
# """ An iterator of the Units collection """
# for unit in self._units:
# yield unit
# def __delitem__(self,variable):
# """ Remove the variable from the units list. """
# for i in range(len(self)):
# if self._units[i].variable == variable:
# del self._units[i]
# return
# # We need to overwrite the copy mechanisms.
# # It really is a pain in the ass, but it works.
# # Luckily all copying need only be refered in the Unit-object.
# def __copy__(self):
# units = Units()
# for unit in self:
# units.append(copy(unit))
# return units
# def __deepcopy__(self, memo):
# units = Units()
# for unit in self:
# units.append(deepcopy(unit))
# return units
# # Do NOT implement a 'convert' method. It could potentially lead to unexpected behaviour as the
# # Unit-object needs to handle this....
# # TODO consider the conversion of a list of Unit-objects via the Units-object.
# class UnitObject(object):
# """
# Contains relevant information about units etc.
# """
# def convert(self,*units):
# """
# Convert all entries in the object to the desired
# units given by the input.
# """
# # Go back in the units variable does not exist.
# if not '_units' in self.__dict__: return
# # If it is a Units object, we can simply loop and do the recursive conversion.
# if isinstance(units[0],Units):
# for unit in units[0]:
# self.convert(unit)
# return
# # First convert all variables associated with a type... ('length',etc.)
# # This well enable one to convert all of length but still have a unit conversion of a
# # single length variable to another.
# for unit in units:
# u = Unit(unit)
# if not u.variable:
# for self_u in self._units:
# if self_u.type() == u.type():
# self.__dict__[self_u.variable] *= self_u.convert(u)
# # Now convert the specific requested units.
# for unit in units:
# u = Unit(unit)
# self_u = self.unit(u.variable)
# if self_u:
# self.__dict__[self_u.variable] *= self_u.convert(u)
# def unit(self,variable):
# """ Returns the unit that is associated with the variable """
# return self._units.unit(variable)
# @property
# def units(self):
# """ Returns the units that is associated with the variable """
# return self._units
# class Variable_ndarray(_np.ndarray):
# """
# Numpy array with automatic unit conversion.
# When two arrays are multiplied we can automatically
# detect units and convert to the correct units.
# Creating a variable with Variable_ndarray we gain access
# to convert which can convert the unit of the variable.
# """
# def convert(self,unit):
# """
# Convert all entries in the object to the desired
# units given by the input.
# """
# # Go back in the units variable does not exist.
# if not '_units' in self.__dict__: return
# # If it is a Units object,
# # we can simply loop and do the recursive conversion.
# if isinstance(unit,Units):
# for u in unit:
# self.convert(u)
# return
# # Ensure that unit is a Unit
# u = Unit(unit)
# # Loop over all variables in this object.
# # It only has one
# for i in self._units:
# if i.type() == u.type():
# self[:] *= i.convert(u)
# def add_unit(self,var,unit):
# """ Adds a unit to a variable beloning to the object """
# def unit(self,variable='self'):
# """ Returns the unit that is associated with the variable """
# return self._units.unit(variable)
# @property
# def units(self):
# """ Returns the units that is associated with the variable """
# return self._units
# @staticmethod
# def _N(array):
# return _np.array(array)
# def __array_finalize__(self,obj):
# """ Finalize the array with the object """
# if obj is None: return
# # Create the default units, we need to copy them, to ensure
# # that we do not attach the same objects.
# if hasattr(obj,'_units'):
# self._units = deepcopy(obj._units)
# else:
# self._units = deepcopy(self._UNITS)
# if hasattr(self,'__variable_finalize__'):
# self.__variable_finalize__()