Source code for moldesign.units.unitsystem

# Copyright 2016 Autodesk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .constants import *


[docs]class UnitSystem(object): """ Class for standardizing units - specifies preferred units for length, mass, energy etc. In MDT, many methods will automatically convert output using the UnitSystem at ``moldesign.units.default`` """ def __init__(self, length, mass, time, energy, temperature=kelvin, force=None, momentum=None, angle=radians, charge=q_e): self.length = length self.mass = mass self.time = time self.energy = energy self.temperature = temperature self.force = force self.momentum = momentum self.angle = angle self.charge = charge def __getitem__(self, item): """ For convenience when using pint dimensionality descriptions. This aliases self['item'] = self['[item]'] = self.item, e.g. self['length'] = self['[length]'] = self.length """ itemname = item.lstrip('[').rstrip(']') return getattr(self, itemname) @property def force(self): if self._force is None: return self.energy / self.length else: return self._force @force.setter def force(self, f): self._force = f @property def momentum(self): if self._momentum is None: return self.mass * self.length / self.time else: return self._momentum @momentum.setter def momentum(self, f): self._momentum = f
[docs] def convert(self, quantity): """ Convert a quantity into this unit system Args: quantity (MdtQuantity): quantity to convert """ baseunit = self.get_baseunit(quantity) if isinstance(baseunit, int): assert baseunit == 1 return quantity * ureg.dimensionless else: result = quantity.to(baseunit) return result
[docs] def get_baseunit(self, quantity): # TODO: this needs to deal with angles # if quantity.dimensionless: return 1.0 # don't call this - it's super-slow (Pint 0.6.1) try: dims = dict(quantity.dimensionality) except AttributeError: try: return self.get_baseunit(quantity[0]) except (IndexError, TypeError): # Assume dimensionless return 1 baseunit = 1 # Factor out energy units (this doesn't work for things like energy/length) if '[length]' in dims and '[mass]' in dims and '[time]' in dims: while dims['[length]'] >= 1 and dims['[mass]'] >= 1 and dims['[time]'] <= -2: baseunit *= self['energy'] dims['[length]'] -= 2 dims['[mass]'] -= 1 dims['[time]'] += 2 if '[current]' in dims: dims.setdefault('[charge]', 0) dims.setdefault('[time]', 0) dims['[charge]'] += dims['[current]'] dims['[time]'] -= dims['[current]'] dims.pop('[current]') # Otherwise, just use the units for unit in dims: if dims[unit] == 0: continue try: baseunit *= self[unit]**dims[unit] except AttributeError: baseunit *= ureg[unit]**dims[unit] return baseunit
default = UnitSystem(length=angstrom, mass=amu, time=fs, energy=eV) atomic_units = UnitSystem(length=a0, mass=m_e, time=t0, energy=hartree) nano_si = UnitSystem(length=nm, mass=dalton, time=fs, energy=kjpermol)