units-system 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +10 -0
- data/Gemfile.lock +29 -0
- data/README.rdoc +2 -2
- data/Rakefile +21 -30
- data/VERSION +1 -1
- data/lib/units-system.rb +8 -648
- data/lib/units/definitions.rb +99 -0
- data/lib/units/math.rb +49 -0
- data/lib/units/measure.rb +263 -0
- data/lib/units/prefixes.rb +42 -0
- data/lib/units/system.rb +236 -0
- data/test/test_units-system.rb +46 -1
- data/units-system.gemspec +40 -28
- metadata +109 -55
- data/.gitignore +0 -21
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
git (1.2.5)
|
5
|
+
jeweler (1.8.3)
|
6
|
+
bundler (~> 1.0)
|
7
|
+
git (>= 1.2.5)
|
8
|
+
rake
|
9
|
+
rdoc
|
10
|
+
json (1.6.6)
|
11
|
+
modalsupport (0.8.3)
|
12
|
+
rake (0.9.2.2)
|
13
|
+
rdoc (3.12)
|
14
|
+
json (~> 1.4)
|
15
|
+
shoulda (3.0.1)
|
16
|
+
shoulda-context (~> 1.0.0)
|
17
|
+
shoulda-matchers (~> 1.0.0)
|
18
|
+
shoulda-context (1.0.0)
|
19
|
+
shoulda-matchers (1.0.0)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
bundler (~> 1)
|
26
|
+
jeweler (~> 1.8.3)
|
27
|
+
modalsupport
|
28
|
+
rdoc (~> 3.12)
|
29
|
+
shoulda
|
data/README.rdoc
CHANGED
@@ -59,10 +59,10 @@ local variables.
|
|
59
59
|
# Now let's convert som imperial units to SI:
|
60
60
|
puts (100*mi/h).to_si # => 44.704*m/s
|
61
61
|
|
62
|
-
# Note that in is a Ruby keyword, so to use inches you must use self.in
|
62
|
+
# Note that +in+ is a Ruby keyword, so to use inches you must use +self.in+:
|
63
63
|
puts (10*cm).to(self.in) # => 3.93700787401575*in
|
64
64
|
# ...or use tha alternative nonstandard name +inch+
|
65
|
-
puts (10*cm).to(
|
65
|
+
puts (10*cm).to(inch) # => 3.93700787401575*inch
|
66
66
|
|
67
67
|
# Now let's use derived units, e.g. power units:
|
68
68
|
x = 10*kW
|
data/Rakefile
CHANGED
@@ -1,21 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
2
12
|
require 'rake'
|
3
13
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
-
end
|
15
|
-
Jeweler::GemcutterTasks.new
|
16
|
-
rescue LoadError
|
17
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = "units-system"
|
17
|
+
gem.summary = %Q{Arithmetic with units of measure}
|
18
|
+
gem.description = %Q{Experimental unit conversion & arithmetic for Ruby 1.9}
|
19
|
+
gem.email = "jgoizueta@gmail.com"
|
20
|
+
gem.homepage = "http://github.com/jgoizueta/units-system"
|
21
|
+
gem.authors = ["Javier Goizueta"]
|
22
|
+
# dependencies defined in Gemfile
|
18
23
|
end
|
24
|
+
Jeweler::RubygemsDotOrgTasks.new
|
19
25
|
|
20
26
|
require 'rake/testtask'
|
21
27
|
Rake::TestTask.new(:test) do |test|
|
@@ -24,24 +30,9 @@ Rake::TestTask.new(:test) do |test|
|
|
24
30
|
test.verbose = true
|
25
31
|
end
|
26
32
|
|
27
|
-
begin
|
28
|
-
require 'rcov/rcovtask'
|
29
|
-
Rcov::RcovTask.new do |test|
|
30
|
-
test.libs << 'test'
|
31
|
-
test.pattern = 'test/**/test_*.rb'
|
32
|
-
test.verbose = true
|
33
|
-
end
|
34
|
-
rescue LoadError
|
35
|
-
task :rcov do
|
36
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
task :test => :check_dependencies
|
41
|
-
|
42
33
|
task :default => :test
|
43
34
|
|
44
|
-
require '
|
35
|
+
require 'rdoc/task'
|
45
36
|
Rake::RDocTask.new do |rdoc|
|
46
37
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
38
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/units-system.rb
CHANGED
@@ -1,561 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
require 'modalsupport'
|
4
|
+
require 'units/measure'
|
5
|
+
require 'units/prefixes'
|
6
|
+
require 'units/system'
|
7
|
+
require 'units/math'
|
4
8
|
|
5
9
|
module Units
|
6
|
-
|
7
|
-
class Measure
|
8
10
|
|
9
|
-
def initialize(mag=1.0, units={})
|
10
|
-
@magnitude = mag # rename to value?
|
11
|
-
case units
|
12
|
-
when Symbol
|
13
|
-
uinfo = Units.unit(units)
|
14
|
-
if uinfo
|
15
|
-
if uinfo.dim
|
16
|
-
@units = {uinfo.dim=>[units,1]}
|
17
|
-
else
|
18
|
-
@magnitude *= uinfo.factor
|
19
|
-
@units = {}
|
20
|
-
end
|
21
|
-
else
|
22
|
-
raise ArgumentError,"Invalid symbol for Measure definition: #{units.inspect} "
|
23
|
-
end
|
24
|
-
when Measure
|
25
|
-
@magnitude *= units.magnitude
|
26
|
-
@units = units.units
|
27
|
-
else
|
28
|
-
@units = units
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_reader :magnitude, :units
|
33
|
-
|
34
|
-
# represent in text using Ruby notation
|
35
|
-
def to_s
|
36
|
-
return @magnitude.to_s if magnitude?
|
37
|
-
u_descr = Units.units_descr(@units)
|
38
|
-
"#{@magnitude}*#{u_descr}"
|
39
|
-
end
|
40
|
-
|
41
|
-
# more verbose description (not grammatically perfect)
|
42
|
-
def describe
|
43
|
-
return @magnitude.to_s if magnitude?
|
44
|
-
u_descr = Units.units_descr(@units, true)
|
45
|
-
"#{@magnitude} #{u_descr}"
|
46
|
-
end
|
47
|
-
|
48
|
-
# more natural concise text representation
|
49
|
-
def abr
|
50
|
-
self.to_s.gsub('**','^').tr('*',' ')
|
51
|
-
end
|
52
|
-
|
53
|
-
# decompose compound units
|
54
|
-
def detailed_units(all_levels = false)
|
55
|
-
mag = @magnitude
|
56
|
-
prev_units = self.units
|
57
|
-
units = {}
|
58
|
-
loop do
|
59
|
-
compound = false
|
60
|
-
prev_units.each_pair do |dim, (unit,mul)|
|
61
|
-
ud = Units.unit(unit)
|
62
|
-
if ud.decomposition
|
63
|
-
compound = true
|
64
|
-
mag *= ud.decomposition.magnitude
|
65
|
-
ud.decomposition.units.each_pair do |d, (u,m)|
|
66
|
-
mag *= self.class.combine(units, d, u, m)
|
67
|
-
end
|
68
|
-
else
|
69
|
-
mag *= self.class.combine(units, dim, unit, mul)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
if all_levels && compound
|
73
|
-
prev_units = units
|
74
|
-
units = {}
|
75
|
-
else
|
76
|
-
break
|
77
|
-
end
|
78
|
-
end
|
79
|
-
Measure.new mag, units
|
80
|
-
end
|
81
|
-
|
82
|
-
# decompose to base units
|
83
|
-
def base
|
84
|
-
detailed_units true
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.combine(units, dim, unit, mult)
|
88
|
-
factor = 1
|
89
|
-
if units[dim]
|
90
|
-
u,m = units[dim]
|
91
|
-
if u!=unit
|
92
|
-
factor *= Units.conversion_factor(unit, u)**mult
|
93
|
-
end
|
94
|
-
units[dim] = [u, m+mult]
|
95
|
-
else
|
96
|
-
units[dim] = [unit, mult]
|
97
|
-
end
|
98
|
-
factor
|
99
|
-
end
|
100
|
-
|
101
|
-
def /(other)
|
102
|
-
self * (other.kind_of?(Numeric) ? 1.0/other : other.inverse)
|
103
|
-
end
|
104
|
-
|
105
|
-
def inspect
|
106
|
-
"Measure(#{@magnitude.inspect}, #{@units.inspect})"
|
107
|
-
end
|
108
|
-
|
109
|
-
def *(other)
|
110
|
-
case other
|
111
|
-
when Numeric
|
112
|
-
mag = self.magnitude*other
|
113
|
-
units = self.units
|
114
|
-
else
|
115
|
-
mag = self.magnitude*other.magnitude
|
116
|
-
units = {}
|
117
|
-
(self.units.keys | other.units.keys).each do |dim|
|
118
|
-
other_u = other.units[dim] || [nil,0]
|
119
|
-
this_u = self.units[dim] || [other_u.first,0]
|
120
|
-
# mag *= Units.conversion_factor(this_u.first, other_u.first) if other_u.first
|
121
|
-
mult = this_u.last+other_u.last
|
122
|
-
mag *= Units.conversion_factor(other_u.first, this_u.first)**(other_u.last) if other_u.first
|
123
|
-
units[dim] = [this_u.first, mult]
|
124
|
-
end
|
125
|
-
end
|
126
|
-
units.reject!{|dim,(u,m)| m==0}
|
127
|
-
Measure.new(mag, units)
|
128
|
-
end
|
129
|
-
|
130
|
-
def +(other)
|
131
|
-
Measure.new self.magnitude+other.to(self.units).magnitude, self.units
|
132
|
-
end
|
133
|
-
|
134
|
-
def -(other)
|
135
|
-
self + (-other)
|
136
|
-
end
|
137
|
-
|
138
|
-
def **(n)
|
139
|
-
raise ArgumentError,"Only integer powers are allowed for magnitudes" unless n.kind_of?(Integer)
|
140
|
-
units = @units.dup
|
141
|
-
units.each_pair do |dim, (u, m)|
|
142
|
-
units[dim] = [u, m*n]
|
143
|
-
end
|
144
|
-
Measure.new @magnitude, units
|
145
|
-
end
|
146
|
-
|
147
|
-
def inverse
|
148
|
-
#Measure.new(1.0/@magnitude, @units.map_hash{|unit,mult| [unit, -mult]})
|
149
|
-
units = {}
|
150
|
-
@units.each_pair do |dim, (unit, mult)|
|
151
|
-
units[dim] = [unit, -mult]
|
152
|
-
end
|
153
|
-
Measure.new(1.0/@magnitude, units)
|
154
|
-
end
|
155
|
-
|
156
|
-
def -@
|
157
|
-
Measure.new(-@magnitude, units)
|
158
|
-
end
|
159
|
-
|
160
|
-
def in(other, mode=:absolute)
|
161
|
-
other = Measure.new(1.0, other) unless other.kind_of?(Measure)
|
162
|
-
other = other.base
|
163
|
-
this = self.base
|
164
|
-
dims = this.units.keys | other.units.keys
|
165
|
-
mag = this.magnitude/other.magnitude
|
166
|
-
dims.each do |dim|
|
167
|
-
if !this.units[dim] || !other.units[dim] ||
|
168
|
-
(this.units[dim].last != other.units[dim].last)
|
169
|
-
raise "Inconsistent units #{Units.units_descr(this.units)} #{Units.units_descr(other.units)}"
|
170
|
-
end
|
171
|
-
this_u, mult = this.units[dim]
|
172
|
-
other_u = other.units[dim].first
|
173
|
-
mag *= Units.conversion_factor(this_u, other_u)**mult
|
174
|
-
end
|
175
|
-
if mode!=:relative && dims.size==1 && this.units[dims.first].last==1
|
176
|
-
# consider "level" conversion for biased units (otherwise consider interval or difference values)
|
177
|
-
mag += Units.conversion_bias(this.units[dims.first].first, other.units[dims.first].first)
|
178
|
-
end
|
179
|
-
mag
|
180
|
-
end
|
181
|
-
|
182
|
-
def to(units, mode=:absolute)
|
183
|
-
units = units.u if units.kind_of?(Measure)
|
184
|
-
Measure.new self.in(units, mode), units
|
185
|
-
end
|
186
|
-
|
187
|
-
def si_units
|
188
|
-
units = {}
|
189
|
-
@units.each_pair do |dim, (unit, mult)|
|
190
|
-
si_unit = SI_UNITS[dim]
|
191
|
-
if si_unit.kind_of?(Measure)
|
192
|
-
si_unit.units.each_pair do |d, (u,m)|
|
193
|
-
self.class.combine(units, d, u, m*mult)
|
194
|
-
end
|
195
|
-
else
|
196
|
-
self.class.combine(units, dim, si_unit, mult)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
units
|
200
|
-
end
|
201
|
-
|
202
|
-
def to_si
|
203
|
-
to(si_units)
|
204
|
-
end
|
205
|
-
|
206
|
-
def coerce(other)
|
207
|
-
[Measure.new(other, {}), self]
|
208
|
-
end
|
209
|
-
|
210
|
-
def u # dimension? unit? only_units? strip_units? units_measure?
|
211
|
-
Measure.new(1.0, self.units)
|
212
|
-
end
|
213
|
-
|
214
|
-
# dimension (quantity)
|
215
|
-
def dimension
|
216
|
-
q = nil
|
217
|
-
u = self.base.si_units
|
218
|
-
SI_UNITS.each_pair do |dim, unit|
|
219
|
-
unit = Measure.new(1.0, unit) unless unit.kind_of?(Measure)
|
220
|
-
if unit.base.si_units == u
|
221
|
-
q = dim
|
222
|
-
break
|
223
|
-
end
|
224
|
-
end
|
225
|
-
q
|
226
|
-
end
|
227
|
-
|
228
|
-
def dimensionless?
|
229
|
-
base.units.reject{|d,(u,m)| m==0}.empty?
|
230
|
-
end
|
231
|
-
|
232
|
-
# less strict dimensionless condition (e.g. an angle is not a pure magnitude in this sense)
|
233
|
-
def magnitude?
|
234
|
-
self.units.reject{|d,(u,m)| m==0}.empty?
|
235
|
-
end
|
236
|
-
|
237
|
-
end # Measure
|
238
|
-
|
239
|
-
def Measure(*args)
|
240
|
-
if args.size==1
|
241
|
-
case args.first
|
242
|
-
when Numeric
|
243
|
-
m = args.first
|
244
|
-
u = {}
|
245
|
-
else
|
246
|
-
m = 1.0
|
247
|
-
u = args.first
|
248
|
-
end
|
249
|
-
args = [m,u]
|
250
|
-
end
|
251
|
-
Units::Measure.new(*args)
|
252
|
-
end
|
253
|
-
module_function :Measure
|
254
|
-
|
255
|
-
PREFIXES = {
|
256
|
-
:y=>[1E-24, 'yocto'],
|
257
|
-
:z=>[1E-21, 'zepto'],
|
258
|
-
:a=>[1E-18, 'atto'],
|
259
|
-
:f=>[1E-15, 'femto'],
|
260
|
-
:p=>[1E-12, 'pico'],
|
261
|
-
:n=>[1E-09, 'nano'],
|
262
|
-
:"\302\265"=>[1E-06, 'micro'], # ASCII alternative: u; define it?
|
263
|
-
:m=>[1E-03, 'milli'],
|
264
|
-
:c=>[1E-02, 'centi'],
|
265
|
-
:d=>[1E-01, 'deci'],
|
266
|
-
:da=>[1E1, 'deca'],
|
267
|
-
:h=>[1E02, 'hecto'],
|
268
|
-
:k=>[1E03, 'kilo'],
|
269
|
-
:M=>[1E06, 'mega'],
|
270
|
-
:G=>[1E09, 'giga'],
|
271
|
-
:T=>[1E12, 'tera'],
|
272
|
-
:P=>[1E15, 'peta'],
|
273
|
-
:E=>[1E18, 'exa'],
|
274
|
-
:Z=>[1E21, 'zetta'],
|
275
|
-
:Y=>[1E24, 'yotta']
|
276
|
-
}
|
277
|
-
|
278
|
-
def self.prefix_factor(prefix)
|
279
|
-
pd = PREFIXES[prefix.to_sym]
|
280
|
-
pd && pd.first
|
281
|
-
end
|
282
|
-
|
283
|
-
def self.prefix_name(prefix)
|
284
|
-
pd = PREFIXES[prefix.to_sym]
|
285
|
-
pd && pd.last
|
286
|
-
end
|
287
|
-
|
288
|
-
def self.prefix_factor_and_name(prefix)
|
289
|
-
PREFIXES[prefix]
|
290
|
-
end
|
291
|
-
|
292
|
-
# mathematical functions available to unit blocks are defined here
|
293
|
-
module Math
|
294
|
-
end
|
295
|
-
|
296
|
-
# unit methods and constants are defined here to be injected in units blocks
|
297
|
-
module System
|
298
|
-
|
299
|
-
extend Math
|
300
|
-
|
301
|
-
def self.define(unit)
|
302
|
-
define_var unit
|
303
|
-
PREFIXES.each do |prefix, (factor, name)|
|
304
|
-
define_var "#{prefix}#{unit}".to_sym
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
class <<self
|
309
|
-
private
|
310
|
-
def define_var(name)
|
311
|
-
name_initial = name.to_s[0,1]
|
312
|
-
if name_initial==name_initial.upcase && name_initial!=name_initial.downcase
|
313
|
-
# we could define a method with the requested name, but it would
|
314
|
-
# no be usable without qualification (System.X) or brackets (X())
|
315
|
-
# so we define a constant. But this requires Ruby 1.9 to be useful;
|
316
|
-
# otherwise the constant is not accesible without qualification in units blocks.
|
317
|
-
System.const_set name, Units.Measure(name)
|
318
|
-
end
|
319
|
-
self.class_eval do
|
320
|
-
define_method name do
|
321
|
-
Units.Measure(name)
|
322
|
-
end
|
323
|
-
module_function name
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
end # Units::System
|
329
|
-
|
330
|
-
def units(&blk)
|
331
|
-
Units::System.class_eval(&blk)
|
332
|
-
end
|
333
|
-
alias :u :units
|
334
|
-
module_function :units, :u
|
335
|
-
|
336
|
-
UnitDefinition = Struct.new(:dim, :factor, :name, :decomposition, :bias)
|
337
|
-
|
338
|
-
UNITS = {} # Hash.new{|h,k| h[k]=UnitDefinition.new()}
|
339
|
-
|
340
|
-
# get unit definition
|
341
|
-
def self.unit(unit_symbol)
|
342
|
-
ud = UNITS[unit_symbol]
|
343
|
-
if ud.nil?
|
344
|
-
factor = 1.0
|
345
|
-
if factor_name = PREFIXES[unit_symbol]
|
346
|
-
ud = UnitDefinition.new(nil, *factor_name)
|
347
|
-
else
|
348
|
-
u = unit_symbol.to_s
|
349
|
-
PREFIXES.each_pair do |prefix, (f,name)|
|
350
|
-
prefix = prefix.to_s
|
351
|
-
if u[0...prefix.length] == prefix
|
352
|
-
factor = f
|
353
|
-
ud = UNITS[u[prefix.length..-1].to_sym]
|
354
|
-
if ud
|
355
|
-
ud = ud.dup
|
356
|
-
ud.name = "#{name}#{ud.name}"
|
357
|
-
break
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|
362
|
-
ud.factor *= factor if ud
|
363
|
-
ud.decomposition *= factor if ud && ud.decomposition
|
364
|
-
end
|
365
|
-
raise ArgumentError,"Invalid Units #{unit_symbol}" unless ud
|
366
|
-
ud
|
367
|
-
end
|
368
|
-
|
369
|
-
# Define new units.
|
370
|
-
# Define a base unit (with a factor for conversion to SI units)
|
371
|
-
# Units.define :unit_symbol, 'unit-name', :quantity, si_units_per_this_unit
|
372
|
-
# Define a unit in terms or another (valid for base or derived units)
|
373
|
-
# Units.define :unit_symbol, 'unit-name', value, :in_units
|
374
|
-
# Define a base unit as a measure-expression
|
375
|
-
# Units.define :unit_symbol, 'unit_name', u{...}
|
376
|
-
# Define a derived unit as a measure-expression
|
377
|
-
# Units.define :unit_symbol, 'unit_name', :quantity, u{...}
|
378
|
-
# For base dimensions the SI unit for a quantity must also be stablished with Unit.si_units;
|
379
|
-
# for derived units, SI units are automatically taken to be the first define unit of the quantity
|
380
|
-
# with unitary value in SI base units.
|
381
|
-
def self.define(unit_symbol, name, *args)
|
382
|
-
eqhivalence = nil
|
383
|
-
si_unit = false
|
384
|
-
bias = nil
|
385
|
-
if args.first.kind_of?(Symbol)
|
386
|
-
dim = args.shift
|
387
|
-
if args.first.kind_of?(Numeric)
|
388
|
-
# simple units
|
389
|
-
factor = args.shift
|
390
|
-
factor_units = args.shift
|
391
|
-
if factor_units
|
392
|
-
ud = unit(factor_units)
|
393
|
-
if ud.dim != dim
|
394
|
-
raise ArgumentError, "Inconsistent units #{factor_units} in definition of #{unit_symbol}"
|
395
|
-
end
|
396
|
-
# maybe it was not simple after all...
|
397
|
-
equivalence = factor*ud.decomposition if ud.decomposition
|
398
|
-
factor *= ud.factor
|
399
|
-
end
|
400
|
-
# si_unit = (factor==1.0) # to save si_units definitions # TODO: tolerance?
|
401
|
-
else
|
402
|
-
# compound unit
|
403
|
-
equivalence = args.shift
|
404
|
-
factor = equivalence.to_si.magnitude
|
405
|
-
si_unit = (factor==1.0) # TODO: tolerance?
|
406
|
-
if equivalence.units.empty?
|
407
|
-
# dimensionless compound dimension... (special case for angular units)
|
408
|
-
equivalence = nil
|
409
|
-
end
|
410
|
-
end
|
411
|
-
elsif args.first.kind_of?(Numeric)
|
412
|
-
# unit define in terms of other unit
|
413
|
-
factor = args.shift
|
414
|
-
factor_units = args.shift
|
415
|
-
u = unit(factor_units)
|
416
|
-
dim = u.dim
|
417
|
-
equivalence = factor*u.decomposition if u.decomposition
|
418
|
-
factor *= u.factor
|
419
|
-
bias = args.shift
|
420
|
-
else
|
421
|
-
# unit defined from a measure expression; the dimension must be already known or defined
|
422
|
-
# here (as as symbol preceding the expression).
|
423
|
-
definition = args.shift
|
424
|
-
dim = definition.dimension
|
425
|
-
raise ArgumentError,"To define a new compound unit a dimension must be specified" unless dim
|
426
|
-
equivalence = definition
|
427
|
-
factor = definition.to_si.magnitude
|
428
|
-
# si_unit = (factor==1.0) # to save si_units definitions # TODO: tolerance?
|
429
|
-
end
|
430
|
-
unit_def = UnitDefinition.new(dim, factor, name, equivalence, bias)
|
431
|
-
if UNITS.has_key?(unit_symbol)
|
432
|
-
raise "Redefinition of #{unit_symbol} as #{unit_def} (previously defined as #{UNITS[unit_symbol]})"
|
433
|
-
end
|
434
|
-
UNITS[unit_symbol] = unit_def
|
435
|
-
System.define unit_symbol
|
436
|
-
Units.si_units unit_def.dim, unit_symbol if si_unit && !SI_UNITS.has_key?(unit_def.dim)
|
437
|
-
end
|
438
|
-
|
439
|
-
SI_UNITS = {}
|
440
|
-
|
441
|
-
def self.si_units(dim, unit)
|
442
|
-
SI_UNITS[dim] = unit
|
443
|
-
end
|
444
|
-
|
445
|
-
def self.dimension(u)
|
446
|
-
unit(u).dim
|
447
|
-
end
|
448
|
-
|
449
|
-
def self.conversion_factor(from, to)
|
450
|
-
from_u = unit(from)
|
451
|
-
to_u = unit(to)
|
452
|
-
raise ArgumentError,"Inconsistent Units (#{from}, #{to})" if from_u.dim!=to_u.dim
|
453
|
-
from_u.factor/to_u.factor
|
454
|
-
end
|
455
|
-
|
456
|
-
def self.conversion_bias(from, to)
|
457
|
-
from_u = unit(from)
|
458
|
-
to_u = unit(to)
|
459
|
-
raise ArgumentError,"Inconsistent Units (#{from}, #{to})" if from_u.dim!=to_u.dim
|
460
|
-
factor = from_u.factor/to_u.factor
|
461
|
-
(from_u.bias||0)*factor - (to_u.bias||0)
|
462
|
-
end
|
463
|
-
|
464
|
-
# simple unit name
|
465
|
-
def self.unit_name(u)
|
466
|
-
uinfo = Units.unit(u)
|
467
|
-
uinfo && uinfo.name
|
468
|
-
end
|
469
|
-
|
470
|
-
def self.unit_descr(u, long=false, mult=1)
|
471
|
-
if long
|
472
|
-
u = unit_name(u)
|
473
|
-
if mult!=1
|
474
|
-
case mult
|
475
|
-
when 2
|
476
|
-
"squared #{u}"
|
477
|
-
when 3
|
478
|
-
"cubed #{u}"
|
479
|
-
else
|
480
|
-
"#{u} to the #{mult} power"
|
481
|
-
end
|
482
|
-
else
|
483
|
-
u
|
484
|
-
end
|
485
|
-
else
|
486
|
-
if mult!=1
|
487
|
-
"#{u}**#{mult}"
|
488
|
-
else
|
489
|
-
u.to_s
|
490
|
-
end
|
491
|
-
end
|
492
|
-
end
|
493
|
-
|
494
|
-
def self.units_descr(units, long=false)
|
495
|
-
units = units.values.sort_by{|u,m| -m}
|
496
|
-
pos_units = units.select{|u| u.last>0}
|
497
|
-
neg_units = units.select{|u| u.last<0}
|
498
|
-
times = long ? " " : "*"
|
499
|
-
num = pos_units.map{|u,m| unit_descr(u,long,m)}.join(times)
|
500
|
-
num = "(#{num})" if pos_units.size>1 && !neg_units.empty? && !long
|
501
|
-
den = neg_units.map{|u,m| unit_descr(u,long,-m)}.join("*")
|
502
|
-
den = "(#{den})" if neg_units.size>1 && !long
|
503
|
-
if pos_units.empty?
|
504
|
-
u_descr = "1/#{den}"
|
505
|
-
elsif neg_units.empty?
|
506
|
-
u_descr = num
|
507
|
-
else
|
508
|
-
connector = long ? " per " : "/"
|
509
|
-
u_descr = "#{num}#{connector}#{den}"
|
510
|
-
end
|
511
|
-
u_descr
|
512
|
-
end
|
513
|
-
|
514
|
-
module Math
|
515
|
-
|
516
|
-
[:sin, :cos, :tan].each do |fun|
|
517
|
-
define_method fun do |x|
|
518
|
-
x = Units.u{x.in(rad)} if x.kind_of?(Measure)
|
519
|
-
::Math.send(fun, x)
|
520
|
-
end
|
521
|
-
module_function fun
|
522
|
-
end
|
523
|
-
|
524
|
-
[:asin, :acos, :atan].each do |fun|
|
525
|
-
define_method fun do |x|
|
526
|
-
if x.kind_of?(Measure)
|
527
|
-
raise ArgumentError,"Invalid dimensions for #{fun} argument" unless x.dimensionless?
|
528
|
-
x = x.magnitude
|
529
|
-
end
|
530
|
-
Units.u{::Math.send(fun, x)*rad}
|
531
|
-
end
|
532
|
-
module_function fun
|
533
|
-
end
|
534
|
-
|
535
|
-
module_function
|
536
|
-
def atan2(x,y)
|
537
|
-
if x.kind_of?(Measure)
|
538
|
-
if y.kind_of?(Measure)
|
539
|
-
if x.base.to_si.units != y.base.to_si.units
|
540
|
-
raise ArgumentError,"Inconsistent units for atan2 arguments #{x.u}, #{y.u}"
|
541
|
-
end
|
542
|
-
# or x = x.to_si.magnitude, y=y.to_si.magnitude
|
543
|
-
y = y.in(x.units)
|
544
|
-
x = x.magnitude
|
545
|
-
else
|
546
|
-
raise ArgumentError,"Invalid dimensions for atan2 argument #{x.u}" unless x.dimensionless?
|
547
|
-
x = x.magnitude
|
548
|
-
end
|
549
|
-
elsif y.kind_of?(Measure)
|
550
|
-
raise ArgumentError,"Invalid dimensions for atan2 argument #{y.u}" unless y.dimensionless?
|
551
|
-
y = y.magnitude
|
552
|
-
end
|
553
|
-
Units.u{::Math.atan2(x,y)*rad}
|
554
|
-
end
|
555
|
-
|
556
|
-
end
|
557
|
-
|
558
|
-
|
559
11
|
# This must be included in any module or class from which units expressions
|
560
12
|
# are to be used in units or u blocks.
|
561
13
|
# It is not needed in Ruby 1.9.1 due to they way constant loop-up is done in that version,
|
@@ -571,101 +23,9 @@ module Units
|
|
571
23
|
end
|
572
24
|
end
|
573
25
|
end
|
574
|
-
|
575
|
-
include UseBlocks
|
576
|
-
|
577
|
-
# Units definitions
|
578
|
-
|
579
|
-
# declare SI base units
|
580
|
-
si_units :mass, :kg # m
|
581
|
-
si_units :length, :m # l
|
582
|
-
si_units :time, :s # t
|
583
|
-
si_units :electric_current, :A # I
|
584
|
-
si_units :temperature, :K # T
|
585
|
-
si_units :luminous_intensity, :cd # Iv
|
586
|
-
si_units :amount_of_substance, :mol # n
|
587
|
-
|
588
|
-
# define base units
|
589
|
-
define :g, 'gram', :mass, 1E-3
|
590
|
-
define :m, 'meter', :length, 1.0
|
591
|
-
define :s, 'second', :time, 1.0
|
592
|
-
define :A, 'ampere', :electric_current, 1.0
|
593
|
-
define :K, 'kelvin', :temperature, 1.0
|
594
|
-
define :cd,'candela',:luminous_intensity, 1.0
|
595
|
-
define :mol,'mole', :amount_of_substance, 1.0
|
596
|
-
|
597
|
-
# declare derived quantities with no named units
|
598
|
-
si_units :speed, u{m/s}
|
599
|
-
si_units :acceleration, u{m/s**2}
|
600
|
-
si_units :area, u{m**2}
|
601
|
-
si_units :volume, u{m**3}
|
602
|
-
|
603
|
-
# derived quantities with named units
|
604
|
-
define :W, 'Watt', :power, u{kg*m**2/s**3} # J/s
|
605
|
-
define :Hz, 'herz', :frequency, u{1/s}
|
606
|
-
define :N, 'newton', :force, u{m*kg/s**2}
|
607
|
-
define :Pa, 'pascal', :pressure, u{N/m**2}
|
608
|
-
define :J, 'joule', :energy, u{N*m}
|
609
|
-
define :C, 'coulomb', :electric_charge, u{s*A}
|
610
|
-
define :V, 'volt', :voltage, u{W/A}
|
611
|
-
define :F, 'farad', :electric_capacitance, u{C/V}
|
612
|
-
define :Ω, 'ohm', :electric_resistance, u{V/A} # ohm: Ω omega: Ω
|
613
|
-
define :S, 'siemens', :electric_condctance, u{1/Ω}
|
614
|
-
define :Wb, 'weber', :magnetic_flux, u{J/A}
|
615
|
-
define :T, 'tesla', :magnetic_field, u{N/(A*m)}
|
616
|
-
define :H, 'henry', :inductance, u{Wb/A}
|
617
|
-
define :rad, 'radian', :angle, u{m/m}
|
618
|
-
define :sr, 'steradian', :solid_angle, u{m**2/m**2}
|
619
|
-
define :lm, 'lumen', :luminous_flux, u{cd*sr}
|
620
|
-
define :lx, 'lux', :illuminance, u{lm/m**2}
|
621
|
-
define :Bq, 'bequerel', :radioactivity, u{1/s}
|
622
|
-
define :Gy, 'gray', :absorbed_dose, u{J/kg}
|
623
|
-
define :Sv, 'sievert', :equivalent_dose, u{J/kg}
|
624
|
-
define :kat, 'katal', :catalytic_activity, u{mol/s}
|
625
|
-
|
626
|
-
# Other units
|
627
|
-
|
628
|
-
define :min, 'minute', 60, :s
|
629
|
-
define :h, 'hour', 60, :min
|
630
|
-
define :d, 'day', 24, :h
|
631
|
-
define :mi, 'mile', 1.609344, :km
|
632
|
-
define :in, 'inch', 2.54, :cm
|
633
|
-
define :ft, 'foot', 0.3048, :m
|
634
|
-
define :inch, 'inch', 1, :in # alternative to avoid having to use self.in (in is a Ruby keyword)
|
635
|
-
define :lb, 'pound', 0.45359237, :kg
|
636
26
|
|
637
|
-
|
638
|
-
define :°F, 'degree Fahrenheit', 5.0/9.0, :K, +459.67
|
639
|
-
define :R, 'rankine', 5.0/9.0, :K
|
640
|
-
|
641
|
-
define :l, 'litre', u{dm**3}
|
642
|
-
define :L, 'litre', 1, :l
|
643
|
-
|
644
|
-
define :°, 'degree', ::Math::PI/180.0, :rad
|
645
|
-
define :′, 'arc-minute', ::Math::PI/180.0/60.0, :rad
|
646
|
-
define :″, 'arc-second', ::Math::PI/180.0/3600.0, :rad
|
647
|
-
# not so cool, but easier to type alternatives:
|
648
|
-
define :deg, 'degree', 1, :°
|
649
|
-
define :arcmin, 'arc-minute', 1, :′
|
650
|
-
define :arcsec, 'arc-second', 1, :″
|
651
|
-
|
652
|
-
define :g0, 'standard gravity', u{9.80665*m/s**2}
|
653
|
-
|
654
|
-
define :bar, 'bar', 1E5, :Pa
|
655
|
-
define :atm, 'atmosphere', 101325.0, :Pa
|
656
|
-
define :mWC, 'meters of water column', u{1E3*kg*g0/m**2}
|
657
|
-
define :Torr, 'torricelli', u{atm/760}
|
658
|
-
define :mHg, 'mHg', u{13.5951E3*kg*g0/m**2}
|
659
|
-
|
660
|
-
# define :kp, 'kilopond', :force, u{kg*g0} # or define pond?
|
661
|
-
define :gf, 'gram-force', u{g*g0} # kilopond kp = kgf
|
662
|
-
define :lbf, 'pound-force', u{lb*g0}
|
663
|
-
|
664
|
-
define :dyn, 'dyne', 10, :µN # u{1*g*cm/s**2}
|
665
|
-
define :galUS, 'U.S. liquid gallon', u{231*self.in**3}
|
666
|
-
define :galUK, 'Imperial gallon', 4.546092, :l
|
667
|
-
define :hp, 'horsepower', u{550*ft*lbf/s}
|
668
|
-
|
669
|
-
define :psi, 'pounds-force per square inch', u{lbf/self.in**2}
|
27
|
+
include UseBlocks
|
670
28
|
|
671
29
|
end # Units
|
30
|
+
|
31
|
+
require 'units/definitions'
|