ISCE_INSAR/components/iscesys/Parsers/rdf/units/physical_quantity.py

510 lines
12 KiB
Python
Raw Normal View History

2019-01-16 19:40:08 +00:00
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright 2014 California Institute of Technology. ALL RIGHTS RESERVED.
#
# 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.
#
# United States Government Sponsorship acknowledged. This software is subject to
# U.S. export control laws and regulations and has been classified as 'EAR99 NLR'
# (No [Export] License Required except when exporting to an embargoed country,
# end user, or in support of a prohibited end use). By downloading this software,
# the user agrees to comply with all applicable U.S. export laws and regulations.
# The user has the responsibility to obtain export licenses, or other export
# authority as may be required before exporting this software to any 'EAR99'
# embargoed foreign country or citizen of those countries.
#
# Author: Eric Belz
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## \namespace rdf.units.physical_quantity Classes for Physical Quantities
import abc
import operator
import sys
## use lower case IFF (prolly deprecated)
_LOWER = False
## Abbreviation case converter
_case = operator.methodcaller("lower") if _LOWER else lambda dum: dum
## This class is a class decorator factory that makes an instance
## of the class with a decorated prefix
class Prefix(object):
"""prefix = Prefix("symbol", exponent)
INPUT:
symbol The prefix string symbol, e.g: "M" for mega
exponent The exponent for the factor... 6 for 10**6
OUTPUT:
prefix A class decorator that creates a new instance of
the decorated class (that must be a sub-class of Unit)
See Unit.__doc__ for why that works.
@prefix
class Dimension(Unit)
si_unit = <what ever measure dimension>
Note: Of course, you can stack them up.
"""
## Without a self.base, this class is no good
__metaclass__ = abc.ABCMeta
## Sub's need a base to define what their prefixing means
@abc.abstractproperty
def base(self):
pass
@abc.abstractmethod
def cast(self):
return self.factor
## Construct with a symbol and in exponent
## \param symbol A string symbol that IS the abbreviation
## \param exponent sets the scale factor = base ** exponent
def __init__(self, symbol, exponent):
## The prefix's official symbol
self.symbol = str(symbol)
## \f$ f = B^x \f$
self.factor = self.base ** exponent
return None
## str(prefix) is the prefix's symbol.
def __str__(self):
return self.symbol
## Class decorator:
## \param cls A Unit sub-class
## \par Side Effects:
## instaniate deocrated intance and loh into Glossary
## \retval cls Class decorators return classes.
def __call__(self, cls):
"""prefix(cls)-->cls'
with SIDE EFFECTS"""
# instansiate class with deocrated instance
cls(str(self) + cls.si_unit, self.cast()(self))
return cls
## <a href="http://en.wikipedia.org/wiki/Metric_prefix">Metric</a> Prefix.
class MetricPrefix(Prefix):
"""Prefix based on 10"""
## Metric is a Perfect 10
base = 10
def __float__(self):
return float(self.factor)
## cast to float
def cast(self):
return float
## <a href="http://en.wikipedia.org/wiki/Binary_prefix">Binary</a> Prefix
## Note: limits/dIfferences of/between JEDEC and IEC
class BinaryPrefix(Prefix):
"""Prefix based on 1024"""
## \f$ 2^{10} \f$
base = 1024
## cast to ling
def __int__(self):
return int(self.factor)
def cast(self):
return int
## <a href="http://en.wikipedia.org/wiki/Yotta-">\f$10^{24}\f$</a>
yotta = MetricPrefix('Z', 24)
## <a href="http://en.wikipedia.org/wiki/Zetta-">\f$10^{21}\f$</a>
zetta = MetricPrefix('Z', 21)
## <a href="http://en.wikipedia.org/wiki/Exa-">\f$10^{18}\f$</a>
exa = MetricPrefix('E', 18)
## <a href="http://en.wikipedia.org/wiki/Peta-">\f$10^{15}\f$</a>
peta = MetricPrefix('P', 15)
## <a href="http://en.wikipedia.org/wiki/Tera-">\f$10^{12}\f$</a>
tera = MetricPrefix('T', 12)
## <a href="http://en.wikipedia.org/wiki/Giga-">\f$10^9\f$</a>
giga = MetricPrefix('G', 9)
## <a href="http://en.wikipedia.org/wiki/Mega-">\f$10^6\f$</a>
mega = MetricPrefix('M', 6)
## <a href="http://en.wikipedia.org/wiki/Kilo-">\f$10^3\f$</a>
kilo = MetricPrefix('k', 3)
## <a href="http://en.wikipedia.org/wiki/Hecto-">\f$10^2\f$</a>
hecto = MetricPrefix('h', 2)
## <a href="http://en.wikipedia.org/wiki/Deca-">\f$10^1\f$</a>
deca = MetricPrefix('da', 1)
## Trival (but it does create an instance and put it in Unit.Glossary
base = MetricPrefix('', 0)
## <a href="http://en.wikipedia.org/wiki/Deci-">\f$10^{-1}\f$</a>
deci = MetricPrefix('d', -1)
## <a href="http://en.wikipedia.org/wiki/Centi-">\f$10^{-2}\f$</a>
centi = MetricPrefix('c', -2)
## <a href="http://en.wikipedia.org/wiki/Milli-">\f$10^{-3}\f$</a>
milli = MetricPrefix('m', -3)
## <a href="http://en.wikipedia.org/wiki/Micro-">\f$10^{-6}\f$</a>\n
## (NB: \f$"u"\f$ is used instead of \f$"\mu"\f$ for typographical reasons)
micro = MetricPrefix('u', -6)
## <a href="http://en.wikipedia.org/wiki/Nano-">\f$10^{-9}\f$</a>
nano = MetricPrefix('n', -9)
## <a href="http://en.wikipedia.org/wiki/Pico-">\f$10^{-12}\f$</a>
pico = MetricPrefix('p', -12)
## <a href="http://en.wikipedia.org/wiki/Femto-">\f$10^{-15}\f$</a>
femto = MetricPrefix('f', -15)
## <a href="http://en.wikipedia.org/wiki/Atto-">\f$10^{-18}\f$</a>
atto= MetricPrefix('a', -18)
## <a href="http://en.wikipedia.org/wiki/Zepto-">\f$10^{-21}\f$</a>
zepto = MetricPrefix('z', -21)
## <a href="http://en.wikipedia.org/wiki/Yocto-">\f$10^{-24}\f$</a>
yocto = MetricPrefix('y', -24)
## Trival (integer measurement)
base2 = BinaryPrefix('', 0)
## \f$ 2^{10} \f$, JEDEC
kilo2 = BinaryPrefix('k', 1)
## \f$ (2^{10})^2 \f$, JEDEC
mega2 = BinaryPrefix('M', 2)
## \f$ (2^{10})^3 \f$, JEDEC
giga2 = BinaryPrefix('G', 3)
## \f$ 2^{10} \f$, IEC
kibi = BinaryPrefix('Ki', 1)
## \f$ (2^{10})^2 \f$, IEC
mebi = BinaryPrefix('Mi', 2)
## \f$ (2^{10})^3 \f$, IEC
gibi = BinaryPrefix('Gi', 3)
## \f$ (2^{10})^4 \f$, IEC
tebi = BinaryPrefix('Ti', 4)
## \f$ (2^{10})^5 \f$, IEC
pebi = BinaryPrefix('Pi', 5)
## \f$ (2^{10})^6 \f$, IEC
exbi = BinaryPrefix('Ei', 6)
## \f$ (2^{10})^7 \f$, IEC
zebi = BinaryPrefix('Zi', 7)
## \f$ (2^{10})^8 \f$, IEC
yebi = BinaryPrefix('Yi', 8)
## The Unit class memoizes its instances
class Unit(str):
"""Unit(value, multiplier=1, adder=0 [,si_unit=None])
On Units and Prefixes:
Instances of the Prefix class deocrate Unit classes- and as such
create instances of:
Sym = <Prefix> + <Unit> when the Unit subclass is created (at import).
That instance is, of course, also a <str> and is memoized in
Unit.Glossary
dictionary as:
{Sym : Sym}
At fist, that looks odd. The point is to do a hash-table search (not a list
search) in the Glossary with "Sym" as a key-- here "Sym" is the ordinary
string supplied by the RDF file's (unit) field.
the resulting Value converts units to <Unit>'s si_unit with <unit>.factor
as a scaling.
Hence, of you're talking float(x) "km", you get:
Glossary["km"](x) --> 1000*x, "m"
"""
__metaclass__ = abc.ABCMeta
## When ever a unit is instantiated, it goes into here.
Glossary = {}
## This is the target unit (SI or not) for all things in Unit subclass.
@abc.abstractproperty
def si_unit(self):
pass
## The conversion function defined: \n
## \f$ y = m(x + b) \f$ \n
# \param m is the multiplier for the conversion
# \param b is the adder (applied 1st).
# \par Side Effects:
# Instance is _memoized()'d.
# \returns A string that can be looked up with a str in a hash-table
# and can then do unit conversion.
def __new__(cls, string="", multiplier=1, adder=0, si_unit=None):
"""string="", multiplier=1, adder=0, si_unit=None):"""
self = str.__new__(cls, _case(string) or si_unit or cls.si_unit)
self._multiplier = multiplier
self._adder = adder
# Allow creation of a new unit that is not derivative of a module cnst.
if si_unit is not None: # Guard on keyword option
self.si_unit = str(si_unit)
## All new instances get memoized
self._memoize()
return self
## Memoize into Unit.Glossary
def _memoize(self, warn=True):
"""save self into Glossary, w/ overwite warning option"""
# check key or not?
if warn and self in self.Glossary: # Guard
print >> sys.stderr, (
'Warning: Overwriting Unit.Glossary["%s"]' % self
)
self.Glossary.update({self:self})
## The conversion function called: \n
## \f$ y = m(x + b) \f$ \n
## \param x is the value in non-base/SI units, and must support float()
## \retval y is the value in self.__class__.si_unit
def __call__(self, x):
# todo: case x? who has case?
return self._multiplier * float(x) + self._adder
## \param index Key to delete
## \par Side Effects:
## deletes key from rdf.units.GLOSSARY for ever.
@classmethod
def __delitem__(cls, index):
del cls.Glossary[index]
## This is a TypeError: only Prefix.init can set Unit.Glossary
@classmethod
def __setitem__(cls, index, value):
raise TypeError("Only Instaniation can set items for % class" %
cls.__name__)
## Length conversion to meters
@exa
@peta
@tera
@giga
@mega
@kilo
@base
@centi
@milli
@micro
@nano
@pico
@femto
@atto
class Length(Unit):
si_unit = 'm'
## Conversion to kilograms
@base
class Mass(Unit):
si_unit = 'kg'
@exa
@peta
@tera
@giga
@mega
@kilo
@base
@milli
@micro
@nano
@pico
@femto
@atto
## Time conversion to seconds
class Time(Unit):
si_unit = 's'
@exa
@peta
@tera
@giga
@mega
@kilo
@milli
@micro
@nano
@pico
@femto
@atto
@base
class ElectricCurrent(Unit):
si_unit = 'amp'
## Length conversion to square-meter
@base
class Area(Unit):
si_unit = 'm*m'
## Speed conversion to meters per seconds
@base
@centi
@kilo
class Velocity(Unit):
si_unit = 'm/s'
## Power conversion to Watts
@exa
@peta
@tera
@giga
@mega
@kilo
@milli
@micro
@nano
@pico
@femto
@atto
class Power(Unit):
si_unit = 'W'
## decibel Power -is not power- it's just a number.
@base
class dBPower(Unit):
si_unit = 'dbW'
## Blaise Pascal (19 June 1623 - 19 August 1662)
@base
class Pressure(Unit):
"""Pascal"""
si_unit = 'Pa'
## Frequency conversion to Hz
@kilo
@mega
@giga
@tera
@base
class Frequency(Unit):
si_unit = 'Hz'
## Temperature conversion to Celcius
@base
class Temperature(Unit):
## This just not right
si_unit = 'degC'
@base
class AmountOfSubstance(Unit):
si_unit = "mol"
@base
class LuminousIntensity(Unit):
si_unit = "cd"
## Angle conversion to degrees
@base
@milli
class Angle(Unit):
si_unit = 'rad'
## Data Volume conversion to bits
@base2
@kilo2
@mega2
@giga2
@kibi
@mebi
@gibi
@tebi
@pebi
@exbi
@zebi
@yebi
class Bit(Unit):
si_unit = 'bits'
## Data rate conversion to bps
@base2
@kilo2
@mega2
@giga2
@mebi
@gibi
@tebi
@pebi
@exbi
@zebi
@yebi
class BitPerSecond(Unit):
si_unit = 'bits/s'
## Data Volume conversion to bits
@base2
@kilo2
@mega2
@giga2
@kibi
@mebi
@gibi
@tebi
@pebi
@exbi
@zebi
@yebi
class Byte(Unit):
si_unit = 'byte'
@base
class Pixel(Unit):
si_unit = 'pixel'
## Data rate conversion to bytes per second
@base2
@kilo2
@mega2
@giga2
@mebi
@gibi
@tebi
@pebi
@exbi
@zebi
@yebi
class BytesPerSecond(Unit):
si_unit = 'byte/s'
## TBD
class Ratio(Unit):
pass
## Send these over to addendum.py
__all__ = ('Length', 'Mass', 'Area', 'Time', 'Velocity', 'Power',
'dBPower', 'Frequency', 'Angle', 'Bit', 'BitPerSecond', 'Ratio',
'BytesPerSecond' , 'Temperature', 'Byte', 'Pixel', 'Pressure')