Class: Units::Measure

Inherits:
Object
  • Object
show all
Includes:
ModalSupport::BracketConstructor, ModalSupport::StateEquivalent
Defined in:
lib/units/measure.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Measure

Returns a new instance of Measure.



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

def initialize(*args)
  if args.size==0
    mag = 1.0
    units = {}
  elsif args.size==1
    case args.first
    when Numeric
      mag = args.first
      units = {}
    else
      mag = 1.0
      units = args.first
    end
  elsif args.size==2
    mag, units = args
  else
    raise ArgumentError, "wrong number of arguments (#{args.size} for 0, 1 or 2)"
  end

  @magnitude = mag # rename to value?
  case units
  when Symbol
    uinfo = Units.unit(units)
    if uinfo
      if uinfo.dim
        @units = {uinfo.dim=>[units,1]}
      else
        @magnitude *= uinfo.factor
        @units = {}
      end
    else
      raise ArgumentError,"Invalid symbol for Measure definition: #{units.inspect} "
    end
  when Measure
    @magnitude *= units.magnitude
    @units = units.units
  else
    @units = units
  end
end

Instance Attribute Details

#magnitudeObject (readonly)

Returns the value of attribute magnitude.



51
52
53
# File 'lib/units/measure.rb', line 51

def magnitude
  @magnitude
end

#unitsObject (readonly)

Returns the value of attribute units.



51
52
53
# File 'lib/units/measure.rb', line 51

def units
  @units
end

Class Method Details

.combine(units, dim, unit, mult) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/units/measure.rb', line 115

def self.combine(units, dim, unit, mult)
  factor = 1
  if units[dim]
    u,m = units[dim]
    if u!=unit
      factor *= Units.conversion_factor(unit, u)**mult
    end
    units[dim] = [u, m+mult]
  else
    units[dim] = [unit, mult]
  end
  units.delete dim if units[dim].last == 0
  factor
end

Instance Method Details

#*(other) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/units/measure.rb', line 139

def *(other)
  other = Units.units(other) if other.kind_of?(String)
  case other
  when Numeric
    mag = self.magnitude*other
    units = self.units
  else
    mag = self.magnitude*other.magnitude
    units = {}
    (self.units.keys | other.units.keys).each do |dim|
      other_u = other.units[dim] || [nil,0]
      this_u = self.units[dim] || [other_u.first,0]
      # mag *= Units.conversion_factor(this_u.first, other_u.first) if other_u.first
      mult = this_u.last+other_u.last
      mag *= Units.conversion_factor(other_u.first, this_u.first)**(other_u.last) if other_u.first
      units[dim] = [this_u.first, mult]
    end
  end
  units.reject!{|dim,(u,m)| m==0}
  Measure.new(mag, units)
end

#**(n) ⇒ Object

Raises:

  • (ArgumentError)


171
172
173
174
175
176
177
178
# File 'lib/units/measure.rb', line 171

def **(n)
  raise ArgumentError,"Only integer powers are allowed for magnitudes" unless n.kind_of?(Integer)
  units = @units.dup
  units.each_pair do |dim, (u, m)|
    units[dim] = [u, m*n]
  end
  Measure.new @magnitude**n, units
end

#+(other) ⇒ Object



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

def +(other)
  other = Units.units(other) if other.kind_of?(String)
  Measure.new self.magnitude+other.to(self.units).magnitude, self.units
end

#-(other) ⇒ Object



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

def -(other)
  other = Units.units(other) if other.kind_of?(String)
  self + (-other)
end

#-@Object



189
190
191
# File 'lib/units/measure.rb', line 189

def -@
  Measure.new(-@magnitude, units)
end

#/(other) ⇒ Object



130
131
132
133
# File 'lib/units/measure.rb', line 130

def /(other)
  other = Units.units(other) if other.kind_of?(String)
  self * (other.kind_of?(Numeric) ? 1.0/other : other.inverse)
end

#abrObject

more natural concise text representation If a block is passed, it is used to format the numeric magnitudes (Float numbers) (e.g., for localization)

Units.units{3*m/(2*s)}.abr{|v| v.to_s.tr('.',',') } # => "1,5 m/s"


70
71
72
73
74
75
76
77
78
79
# File 'lib/units/measure.rb', line 70

def abr
  txt = self.to_s.gsub('**','^').tr('*',' ')
  if block_given?
    txt.gsub!(/[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)([eE][+-]?(\d+_?)*\d+)?/) do
      v = $&.to_f
      yield v
    end
  end
  txt
end

#baseObject

decompose to base units



111
112
113
# File 'lib/units/measure.rb', line 111

def base
  detailed_units true
end

#coerce(other) ⇒ Object



241
242
243
244
# File 'lib/units/measure.rb', line 241

def coerce(other)
  # other = Units.units(other) if other.kind_of?(String)
  [Measure[other], self]
end

#describeObject

more verbose description (not grammatically perfect)



61
62
63
64
65
# File 'lib/units/measure.rb', line 61

def describe
  return @magnitude.to_s if magnitude?
  u_descr = Units.units_descr(@units, true)
  "#{@magnitude} #{u_descr}"
end

#detailed_units(all_levels = false) ⇒ Object

decompose compound units



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

def detailed_units(all_levels = false)
  mag = @magnitude
  prev_units = self.units
  units = {}
  loop do
    compound = false
    prev_units.each_pair do |dim, (unit,mul)|
      ud = Units.unit(unit)
      if ud.decomposition
        compound = true
        mag *= ud.decomposition.magnitude
        ud.decomposition.units.each_pair do |d, (u,m)|
          mag *= self.class.combine(units, d, u, m*mul)
        end
      else
        mag *= self.class.combine(units, dim, unit, mul)
      end
    end
    if all_levels && compound
      prev_units = units
      units = {}
    else
      break
    end
  end
  Measure.new mag, units
end

#dimensionObject

dimension (quantity)



251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/units/measure.rb', line 251

def dimension
  q = nil
  u = self.base.si_units
  SI_UNITS.each_pair do |dim, unit|
    unit = Measure.new(1.0, unit) unless unit.kind_of?(Measure)
    if unit.base.si_units == u
      q = dim
      break
    end
  end
  q
end

#dimensionless?Boolean

Returns:

  • (Boolean)


264
265
266
# File 'lib/units/measure.rb', line 264

def dimensionless?
  base.units.reject{|d,(u,m)| m==0}.empty?
end

#in(other, mode = :absolute) ⇒ Object



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

def in(other, mode=:absolute)
  other = Units.units(other) if other.kind_of?(String)
  other = Measure.new(1.0, other) unless other.kind_of?(Measure)
  other = other.base
  this = self.base
  dims = this.units.keys | other.units.keys
  mag = this.magnitude/other.magnitude
  dims.each do |dim|
    if !this.units[dim] || !other.units[dim] ||
       (this.units[dim].last != other.units[dim].last)
      raise "Inconsistent units #{Units.units_descr(this.units)} #{Units.units_descr(other.units)}"
    end
    this_u, mult = this.units[dim]
    other_u = other.units[dim].first
    mag *= Units.conversion_factor(this_u, other_u)**mult
  end
  if mode!=:relative && dims.size==1 && this.units[dims.first].last==1
    # consider "level" conversion for biased units (otherwise consider interval or difference values)
    mag += Units.conversion_bias(this.units[dims.first].first, other.units[dims.first].first)
  end
  mag
end

#inspectObject



135
136
137
# File 'lib/units/measure.rb', line 135

def inspect
  "Units::Measure[#{@magnitude.inspect}, #{@units.inspect}]"
end

#inverseObject



180
181
182
183
184
185
186
187
# File 'lib/units/measure.rb', line 180

def inverse
  #Measure.new(1.0/@magnitude, @units.map_hash{|unit,mult| [unit, -mult]})
  units = {}
  @units.each_pair do |dim, (unit, mult)|
    units[dim] = [unit, -mult]
  end
  Measure.new(1.0/@magnitude, units)
end

#magnitude?Boolean

less strict dimensionless condition (e.g. an angle is not a pure magnitude in this sense)

Returns:

  • (Boolean)


269
270
271
# File 'lib/units/measure.rb', line 269

def magnitude?
  self.units.reject{|d,(u,m)| m==0}.empty?
end

#si_unitsObject



222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/units/measure.rb', line 222

def si_units
  units = {}
  @units.each_pair do |dim, (unit, mult)|
    si_unit = SI_UNITS[dim]
    if si_unit.kind_of?(Measure)
      si_unit.units.each_pair do |d, (u,m)|
        self.class.combine(units, d, u, m*mult)
      end
    else
      self.class.combine(units, dim, si_unit, mult)
    end
  end
  units
end

#to(units, mode = :absolute) ⇒ Object



216
217
218
219
220
# File 'lib/units/measure.rb', line 216

def to(units, mode=:absolute)
  units = Units.units(units) if units.kind_of?(String)
  units = units.u if units.kind_of?(Measure)
  Measure.new self.in(units, mode), units
end

#to_sObject

represent in text using Ruby notation



54
55
56
57
58
# File 'lib/units/measure.rb', line 54

def to_s
  return @magnitude.to_s if magnitude?
  u_descr = Units.units_descr(@units)
  "#{@magnitude}*#{u_descr}"
end

#to_siObject



237
238
239
# File 'lib/units/measure.rb', line 237

def to_si
  to(si_units)
end

#uObject

dimension? unit? only_units? strip_units? units_measure?



246
247
248
# File 'lib/units/measure.rb', line 246

def u # dimension? unit? only_units? strip_units? units_measure?
  Measure.new(1.0, self.units)
end