Module: Units

Includes:
UseBlocks
Defined in:
lib/units/math.rb,
lib/units-system.rb,
lib/units/system.rb,
lib/units/measure.rb,
lib/units/prefixes.rb,
lib/units/definitions.rb

Defined Under Namespace

Modules: Const, Math, System, UseBlocks Classes: ConstantDefinition, Measure, UnitDefinition

Constant Summary collapse

UNITS =

Hash.new{|h,k| h=UnitDefinition.new()}

{}
SI_UNITS =
{}
CONSTANTS =
{}
PREFIXES =
{
  :y=>[1E-24, 'yocto'],
  :z=>[1E-21, 'zepto'],
  :a=>[1E-18, 'atto'],
  :f=>[1E-15, 'femto'],
  :p=>[1E-12, 'pico'],
  :n=>[1E-09, 'nano'],
  :"\302\265"=>[1E-06, 'micro'],
  :"u"=>[1E-06, 'micro'],
  :m=>[1E-03, 'milli'],
  :c=>[1E-02, 'centi'],
  :d=>[1E-01, 'deci'],
  :da=>[1E1, 'deca'],
  :h=>[1E02, 'hecto'],
  :k=>[1E03, 'kilo'],
  :M=>[1E06, 'mega'],
  :G=>[1E09, 'giga'],
  :T=>[1E12, 'tera'],
  :P=>[1E15, 'peta'],
  :E=>[1E18, 'exa'],
  :Z=>[1E21, 'zetta'],
  :Y=>[1E24, 'yotta']
}

Class Method Summary collapse

Methods included from UseBlocks

append_features

Class Method Details

.constant(symbol, description = nil, value = nil) ⇒ Object



253
254
255
256
257
258
259
260
# File 'lib/units/system.rb', line 253

def self.constant(symbol, description=nil, value=nil)
  if description.nil? && value.nil?
    c = CONSTANTS[symbol]
    c && c.value
  else
    Const.define symbol, description, value
  end
end

.conversion_bias(from, to) ⇒ Object

Raises:

  • (ArgumentError)


177
178
179
180
181
182
183
# File 'lib/units/system.rb', line 177

def self.conversion_bias(from, to)
  from_u = unit(from)
  to_u = unit(to)
  raise ArgumentError,"Inconsistent Units (#{from}, #{to})" if from_u.dim!=to_u.dim
  factor = from_u.factor/to_u.factor
  (from_u.bias||0)*factor - (to_u.bias||0)
end

.conversion_factor(from, to) ⇒ Object

Raises:

  • (ArgumentError)


170
171
172
173
174
175
# File 'lib/units/system.rb', line 170

def self.conversion_factor(from, to)
  from_u = unit(from)
  to_u = unit(to)
  raise ArgumentError,"Inconsistent Units (#{from}, #{to})" if from_u.dim!=to_u.dim
  from_u.factor/to_u.factor
end

.define(unit_symbol, name, *args) ⇒ Object

Define new units. Define a base unit (with a factor for conversion to SI units)

Units.define :unit_symbol, 'unit-name', :quantity, si_units_per_this_unit

Define a unit in terms or another (valid for base or derived units)

Units.define :unit_symbol, 'unit-name', value, :in_units

Define a base unit as a measure-expression

Units.define :unit_symbol, 'unit_name', u{...}

Define a derived unit as a measure-expression

Units.define :unit_symbol, 'unit_name', :quantity, u{...}

For base dimensions the SI unit for a quantity must also be stablished with Unit.si_units; for derived units, SI units are automatically taken to be the first define unit of the quantity with unitary value in SI base units.



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
156
157
158
# File 'lib/units/system.rb', line 102

def self.define(unit_symbol, name, *args)
  eqhivalence = nil
  si_unit = false
  bias = nil
  if args.first.kind_of?(Symbol)
    dim = args.shift
    if args.first.kind_of?(Numeric)
      # simple units
      factor = args.shift
      factor_units = args.shift
      if factor_units
        ud = unit(factor_units)
        if ud.dim != dim
          raise ArgumentError, "Inconsistent units #{factor_units} in definition of #{unit_symbol}"
        end
        # maybe it was not simple after all...
        equivalence = factor*ud.decomposition if ud.decomposition
        factor *= ud.factor
      end
      # si_unit = (factor==1.0) # to save si_units definitions # TODO: tolerance?
    else
      # compound unit
      equivalence = args.shift
      factor = equivalence.to_si.magnitude
      si_unit = (factor==1.0) # TODO: tolerance?
      if equivalence.units.empty?
        # dimensionless compound dimension... (special case for angular units)
        equivalence = nil
      end
    end
  elsif args.first.kind_of?(Numeric)
    # unit define in terms of other unit
    factor = args.shift
    factor_units = args.shift
    u = unit(factor_units)
    dim = u.dim
    equivalence = factor*u.decomposition if u.decomposition
    factor *= u.factor
    bias = args.shift
  else
    # unit defined from a measure expression; the dimension must be already known or defined
    # here (as as symbol preceding the expression).
    definition = args.shift
    dim = definition.dimension
    raise ArgumentError,"To define a new compound unit a dimension must be specified" unless dim
    equivalence = definition
    factor = definition.to_si.magnitude
    # si_unit = (factor==1.0) # to save si_units definitions # TODO: tolerance?
  end
  unit_def = UnitDefinition.new(dim, factor, name, equivalence, bias)
  if UNITS.has_key?(unit_symbol)
    raise "Redefinition of #{unit_symbol} as #{unit_def} (previously defined as #{UNITS[unit_symbol]})"
  end
  UNITS[unit_symbol] = unit_def
  System.define unit_symbol
  Units.si_units unit_def.dim, unit_symbol if si_unit && !SI_UNITS.has_key?(unit_def.dim)
end

.dimension(u) ⇒ Object



166
167
168
# File 'lib/units/system.rb', line 166

def self.dimension(u)
  unit(u).dim
end

.Measure(*args) ⇒ Object

Measure



275
276
277
# File 'lib/units/measure.rb', line 275

def Measure(*args)
  Measure[*args]
end

.prefix_factor(prefix) ⇒ Object



29
30
31
32
# File 'lib/units/prefixes.rb', line 29

def self.prefix_factor(prefix)
  pd = PREFIXES[prefix.to_sym]
  pd && pd.first
end

.prefix_factor_and_name(prefix) ⇒ Object



39
40
41
# File 'lib/units/prefixes.rb', line 39

def self.prefix_factor_and_name(prefix)
  PREFIXES[prefix]
end

.prefix_name(prefix) ⇒ Object



34
35
36
37
# File 'lib/units/prefixes.rb', line 34

def self.prefix_name(prefix)
  pd = PREFIXES[prefix.to_sym]
  pd && pd.last
end

.si_units(dim, unit) ⇒ Object



162
163
164
# File 'lib/units/system.rb', line 162

def self.si_units(dim, unit)
  SI_UNITS[dim] = unit
end

.uObject

Units::System



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/units/system.rb', line 54

def units(string=nil, &blk)
  if string
    if blk
      raise ArgumentError, "wrong number of arguments (1 for 0)"
    else
      Units::System.class_eval(string)
    end
  else
    Units::System.class_eval(&blk)
  end
end

.unit(unit_symbol) ⇒ Object

get unit definition

Raises:

  • (ArgumentError)


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
# File 'lib/units/system.rb', line 62

def self.unit(unit_symbol)
  ud = UNITS[unit_symbol]
  if ud.nil?
    factor = 1.0
    if factor_name = PREFIXES[unit_symbol]
      ud = UnitDefinition.new(nil, *factor_name)
    else
      u = unit_symbol.to_s
      PREFIXES.each_pair do |prefix, (f,name)|
        prefix = prefix.to_s
        if u[0...prefix.length] == prefix
          factor = f
          ud = UNITS[u[prefix.length..-1].to_sym]
          if ud
            ud = ud.dup
            ud.name = "#{name}#{ud.name}"
            break
          end
        end
      end
    end
    ud.factor *= factor if ud
    ud.decomposition *= factor if ud && ud.decomposition
  end
  raise ArgumentError,"Invalid Units #{unit_symbol}" unless ud
  ud
end

.unit_descr(u, long = false, mult = 1) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/units/system.rb', line 191

def self.unit_descr(u, long=false, mult=1)
  if long
    u = unit_name(u)
    if mult!=1
      case mult
      when 2
        "squared #{u}"
      when 3
        "cubed #{u}"
      else
        "#{u} to the #{mult} power"
      end
    else
      u
    end
  else
    if mult!=1
      "#{u}**#{mult}"
    else
      u.to_s
    end
  end
end

.unit_name(u) ⇒ Object

simple unit name



186
187
188
189
# File 'lib/units/system.rb', line 186

def self.unit_name(u)
  uinfo = Units.unit(u)
  uinfo && uinfo.name
end

.units(string = nil, &blk) ⇒ Object

Units::System



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/units/system.rb', line 43

def units(string=nil, &blk)
  if string
    if blk
      raise ArgumentError, "wrong number of arguments (1 for 0)"
    else
      Units::System.class_eval(string)
    end
  else
    Units::System.class_eval(&blk)
  end
end

.units_descr(units, long = false) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/units/system.rb', line 215

def self.units_descr(units, long=false)
  units = units.values.sort_by{|u,m| -m}
  pos_units = units.select{|u| u.last>0}
  neg_units = units.select{|u| u.last<0}
  times = long ? " " : "*"
  num = pos_units.map{|u,m| unit_descr(u,long,m)}.join(times)
  num = "(#{num})" if pos_units.size>1 && !neg_units.empty? && !long
  den = neg_units.map{|u,m| unit_descr(u,long,-m)}.join("*")
  den = "(#{den})" if neg_units.size>1 && !long
  if pos_units.empty?
    u_descr = "1/#{den}"
  elsif neg_units.empty?
    u_descr = num
  else
    connector = long ? " per " : "/"
    u_descr = "#{num}#{connector}#{den}"
  end
  u_descr
end

.with_constants(*constants, &blk) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/units/system.rb', line 262

def self.with_constants(*constants, &blk)
  m = Module.new
  m.extend Units::System
  m.extend Units::Math
  cap_constants = []
  constants.each do |const|
    m.instance_eval do
      # Ruby 1.9.1 allows this nicer definition:
      #   define_singleton_method(const){Units.constant(const)}
      eigenclass = class<<self; self; end
      eigenclass.instance_eval{define_method(const){Units.constant(const)}}
      name_initial = const.to_s[0,1]
      if name_initial==name_initial.upcase && name_initial!=name_initial.downcase
        cap_constants << const
      end
    end
  end
  UseBlocks.with_constants(*cap_constants) do
    m.instance_eval &blk
  end
end