prefix.py 5.19 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/bin/env python
"""
Module simtk.unit.prefix

"""

__author__ = "Christopher M. Bruns"
__version__ = "0.6"

from baseunit import BaseUnit
from unit import Unit, ScaledUnit
import sys

###################
### SI PREFIXES ###
###################

class SiPrefix(object):
    """
    Unit prefix that can be multiplied by a unit to yield a new unit.
    
    e.g. millimeter = milli*meter
    """
    def __init__(self, prefix, factor, symbol):
        self.prefix = prefix
        self.factor = factor
        self.symbol = symbol
        
    def __mul__(self, unit):
        """
        SiPrefix * BaseUnit yields new BaseUnit
        SiPrefix * ScaledUnit yields new ScaledUnit
        SiPrefix * Unit with exactly one BaseUnit or ScaledUnit yields new Unit
        """
        if isinstance(unit, BaseUnit):
            # BaseUnit version
            symbol = self.symbol + unit.symbol
            name = self.prefix + unit.name
            factor = self.factor
            # TODO - check for existing BaseUnit with same name, symbol, and factor
            new_base_unit = BaseUnit(unit.dimension, name, symbol)
            new_base_unit.define_conversion_factor_to(unit, factor)
            return new_base_unit
        elif isinstance(unit, ScaledUnit):
            # ScaledUnit version
            symbol = self.symbol + unit.symbol
            name = self.prefix + unit.name
            factor = self.factor * unit.factor
            # TODO - check for existing BaseUnit with same name, symbol, and factor
            return ScaledUnit(factor, unit.master, name, symbol)
        elif isinstance(unit, Unit):
            base_units = list(unit.iter_base_or_scaled_units())
            if 1 != len(base_units):
                raise TypeError('Unit prefix "%s" can only be with simple Units containing one component.' % self.prefix)
            if 1 != base_units[0][1]:
                raise TypeError('Unit prefix "%s" can only be with simple Units with an exponent of 1.' % self.prefix)
            base_unit = base_units[0][0]
            # Delegate to Base or Scaled Unit multiply
            new_base_unit = self * base_unit
            new_unit = Unit({new_base_unit: 1.0})
            return new_unit
        else:
            raise TypeError('Unit prefix "%s" can only be applied to a Unit, BaseUnit, or ScaledUnit.' % self.prefix)

yotto = SiPrefix("yotto", 1e-24, 'y')
zepto = SiPrefix("zepto", 1e-21, 'z')
atto  = SiPrefix("atto",  1e-18, 'a')
femto = SiPrefix("femto", 1e-15, 'f')
pico  = SiPrefix("pico",  1e-12, 'p')
nano  = SiPrefix("nano",  1e-9,  'n')
micro = SiPrefix("micro", 1e-6,  'u')
milli = SiPrefix("milli", 1e-3,  'm')
centi = SiPrefix("centi", 1e-2,  'c')
deci  = SiPrefix("deci",  1e-1,  'd')
# two spellings for deka prefix
deka  = SiPrefix("deka",  1e1,   'da')
deca  = SiPrefix("deca",  1e1,   'da')
hecto = SiPrefix("hecto", 1e2,   'h')
kilo  = SiPrefix("kilo",  1e3,   'k')
mega  = SiPrefix("mega",  1e6,   'M')
giga  = SiPrefix("giga",  1e9,   'G')
tera  = SiPrefix("tera",  1e12,  'T')
peta  = SiPrefix("peta",  1e15,  'P')
exa   = SiPrefix("exa",   1e18,  'E')
zetta = SiPrefix("zetta", 1e21,  'Z')
yotta = SiPrefix("yotta", 1e24,  'Y')

si_prefixes = (   yotto
                , zepto
                , atto
                , femto
                , pico
                , nano
                , micro
                , milli
                , centi
                , deci
                , deka
                , deca
                , hecto
                , kilo
                , mega
                , giga
                , tera
                , peta
                , exa
                , zetta
                , yotta)

def define_prefixed_units(base_unit, module = sys.modules[__name__]):
    """
    Create attributes for prefixed units derived from a particular BaseUnit, e.g. "kilometer" from "meter_base_unit"
    
    Parameters
     - base_unit (BaseUnit) existing base unit to use as a basis for prefixed units
     - module (Module) module which will contain the new attributes.  Defaults to simtk.unit module.
    """
    for prefix in si_prefixes:
        new_base_unit = prefix * base_unit
        name = new_base_unit.name
        new_unit = Unit({new_base_unit: 1.0})
        # Create base_unit attribute, needed for creating UnitSystems
        module.__dict__[name + '_base_unit'] = new_base_unit # e.g. "kilometer_base_unit"        
        # Create attribue in this module
        module.__dict__[name] = new_unit # e.g. "kilometer"
        # And plural version
        module.__dict__[name + 's'] = new_unit # e.g. "kilometers"


# Binary prefixes

kibi = SiPrefix("kibi", 2.0**10,  'Ki')
mebi = SiPrefix("mebi", 2.0**20,  'Mi')
gibi = SiPrefix("gibi", 2.0**30,  'Gi')
tebi = SiPrefix("tebi", 2.0**40,  'Ti')
pebi = SiPrefix("pebi", 2.0**50,  'Pi')
exbi = SiPrefix("exbi", 2.0**60,  'Ei')
zebi = SiPrefix("zebi", 2.0**70,  'Zi')
yobi = SiPrefix("yobi", 2.0**80,  'Yi')

binary_prefixes = ( kibi
                  , mebi
                  , gibi
                  , tebi
                  , pebi
                  , exbi
                  , zebi
                  , yobi)


# run module directly for testing
if __name__=='__main__':
    # Test the examples in the docstrings
    import doctest, sys
    doctest.testmod(sys.modules[__name__])