phys-units 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +84 -19
- data/lib/phys/units.rb +1 -0
- data/lib/phys/units/mixin.rb +3 -16
- data/lib/phys/units/parse.rb +1 -1
- data/lib/phys/units/parse.y +1 -1
- data/lib/phys/units/quantity.rb +112 -36
- data/lib/phys/units/unit.rb +77 -74
- data/lib/phys/units/unit_class.rb +32 -15
- data/lib/phys/units/units_mixin.rb +80 -0
- data/lib/phys/units/version.rb +1 -1
- data/spec/unit_spec.rb +3 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0ec7810800a733cda3aa7f753bf10d11b76891d
|
4
|
+
data.tar.gz: b2c93b618d876183ea89ebfab9e6f353a6160746
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0b1ea5529d3017813abe2c056b8eda47db00f47dd3cf0405fd0551182f551bdd95493352ea0ff8ecce1091e5269a593411c09d1caa705514d9c39520e8d54b5
|
7
|
+
data.tar.gz: 7d4bdf5a200c37d8d9cd7b0416bbaa7812a988905b358ea93e88dd635f056226305a6c57bbae0bfe27c48ef7ed9080a64ca91a28fcabc9b66675b4ab082443fd
|
data/README.md
CHANGED
@@ -1,38 +1,63 @@
|
|
1
1
|
# Phys-Units
|
2
2
|
|
3
|
-
GNU Units-compatible library for Ruby.
|
3
|
+
[GNU Units](http://www.gnu.org/software/units/)-compatible library for Ruby.
|
4
4
|
Former name is [Quanty](http://narray.rubyforge.org/quanty/quanty-en.html),
|
5
5
|
the first Ruby units library released in 2001.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
* [GitHub](https://github.com/masa16/phys-units)
|
8
|
+
* [RubyGems](https://rubygems.org/gems/phys-units)
|
9
|
+
* [Class Documentation](http://rubydoc.info/gems/phys-units/frames/)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Install from gem as:
|
14
|
+
|
15
|
+
$ gem install phys-units
|
16
|
+
|
17
|
+
Or install from source tree:
|
18
|
+
|
19
|
+
$ ruby setup.rb
|
20
|
+
|
21
|
+
## Overview
|
22
|
+
|
23
|
+
### Phys::Quantity
|
24
|
+
is the primary class of Phys-Units library, intended to be manipulated by users.
|
25
|
+
This class represents Physical Quantities with a Unit of measurement.
|
26
|
+
It contains *Value* and *Unit*.
|
10
27
|
|
11
28
|
* *Value*
|
12
29
|
must be a class instance having arithmetic methods,
|
13
30
|
but it is not necessary to be a Numeric.
|
14
31
|
This is a duck typing way.
|
32
|
+
|
33
|
+
Phys::Quantity[2.5,"miles"].value #=> 2.5
|
34
|
+
|
15
35
|
* *Unit*
|
16
|
-
is an instance of Phys::Unit class
|
17
|
-
obtained by parsing *expr* string.
|
36
|
+
is an instance of Phys::Unit class obtained by parsing *expr* string.
|
18
37
|
|
19
|
-
|
20
|
-
for more details.
|
38
|
+
Phys::Quantity[2.5,"miles"].unit #=> #<Phys::Unit 1609.344,{"m"=>1},@expr="5280 ft">
|
21
39
|
|
22
|
-
|
40
|
+
### Phys::Unit
|
41
|
+
is a class to represent Physical Units of measurement.
|
42
|
+
This class is used in the Phys::Quantity for calculations with Units,
|
43
|
+
and users do not always need to know its mechanism.
|
44
|
+
It has *Factor* and *Dimension*:
|
23
45
|
|
24
|
-
|
46
|
+
* *Factor* of the unit is a scale factor relative to its base unit.
|
25
47
|
|
26
|
-
|
48
|
+
Phys::Unit["km"].factor #=> 1000
|
27
49
|
|
28
|
-
|
50
|
+
* *Dimension* of the unit
|
51
|
+
is a hash table containing base units and dimensions as key-value pairs.
|
29
52
|
|
30
|
-
|
53
|
+
Phys::Unit["N"].dimension #=> {"kg"=>1, "m"=>1, "s"=>-2}
|
31
54
|
|
32
|
-
|
55
|
+
### Examples
|
33
56
|
|
34
|
-
require
|
57
|
+
require "phys/units"
|
35
58
|
Q = Phys::Quantity
|
59
|
+
U = Phys::Unit
|
60
|
+
|
36
61
|
Q[1.23,'km'] + Q[4.56,'m'] #=> Phys::Quantity[1.23456,'km']
|
37
62
|
Q[123,'mile'] / Q[2,'hr'] #=> Phys::Quantity[61,'mile/hr']
|
38
63
|
Q[61,'miles/hr'].want('m/s') #=> Phys::Quantity[27.26944,'m/s']
|
@@ -41,17 +66,32 @@ Or install from source tree:
|
|
41
66
|
Q[20,'tempC'].want('tempF') #=> Phys::Quantity[68,'tempF']
|
42
67
|
Math.cos(Q[60,'degree'].to_f) #=> 0.5
|
43
68
|
|
69
|
+
U["m/s"] === Q[1,'miles/hr'] #=> true
|
70
|
+
|
71
|
+
case Q[1,"miles/hr"]
|
72
|
+
when U["LENGTH"]
|
73
|
+
puts "length"
|
74
|
+
when U["TIME"]
|
75
|
+
puts "time"
|
76
|
+
when U["VELOCITY"]
|
77
|
+
puts "velocity"
|
78
|
+
else
|
79
|
+
puts "other"
|
80
|
+
end #=> "velocity"
|
81
|
+
|
44
82
|
## Features
|
45
83
|
|
46
|
-
Phys-Units library is
|
84
|
+
Phys-Units library is differentiated from many other units libraries for Ruby,
|
47
85
|
by the following features:
|
48
86
|
|
49
87
|
* Compatible with GNU Units except nonlinear units.
|
50
88
|
* Provides 2331 units, 85 prefixes, including UTF-8 unit names.
|
51
|
-
* Defines Units by reading GNU Units text data
|
89
|
+
* Defines Units by reading GNU Units text data
|
90
|
+
(see [load_units.rb](https://github.com/masa16/phys-units/blob/master/lib/phys/units/load_units.rb)),
|
52
91
|
unlike other libraries which define Units in Ruby code.
|
53
|
-
*
|
54
|
-
|
92
|
+
* Provides orthodox design of class interface.
|
93
|
+
* No addition or modification to Ruby standard classes in standard usage,
|
94
|
+
to avoid conflicts with other libraries.
|
55
95
|
* Calculation of values is through Ruby Numeric arithmetic methods.
|
56
96
|
Phys-Units does not care it.
|
57
97
|
* Conversion factors are internally held in Rational form even
|
@@ -59,6 +99,31 @@ by the following features:
|
|
59
99
|
* PI number has a dimension.
|
60
100
|
* Japanese Units are available by require 'phys/units/jp'.
|
61
101
|
|
102
|
+
## Appendix Feature
|
103
|
+
|
104
|
+
Mix-in feature provides simple calculator of units.
|
105
|
+
Caution: The use of Mix-in may cause conflicts with other library.
|
106
|
+
|
107
|
+
$ irb -rphys/units/mixin
|
108
|
+
|
109
|
+
irb> (2.5.miles/hr).want m/s
|
110
|
+
=> Phys::Quantity[1.1176,"m/s"]
|
111
|
+
|
112
|
+
irb> 23.tempC.want tempF
|
113
|
+
=> Phys::Quantity[73.4,"tempF"]
|
114
|
+
|
115
|
+
irb> print_units LENGTH
|
116
|
+
m !
|
117
|
+
meter m
|
118
|
+
LENGTH meter
|
119
|
+
:
|
120
|
+
|
121
|
+
irb> print_units ENERGY
|
122
|
+
joule N m
|
123
|
+
J joule
|
124
|
+
prout 185.5 keV
|
125
|
+
:
|
126
|
+
|
62
127
|
## Platforms tested
|
63
128
|
|
64
129
|
* ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]
|
data/lib/phys/units.rb
CHANGED
data/lib/phys/units/mixin.rb
CHANGED
@@ -1,18 +1,5 @@
|
|
1
1
|
require "phys/units"
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
# However, this kind of global change will cause unexpected problems.
|
8
|
-
|
9
|
-
class Numeric
|
10
|
-
alias method_missing_orig method_missing
|
11
|
-
def method_missing(method, *args, &block)
|
12
|
-
if unit=Phys::Unit.find_unit(method.to_s)
|
13
|
-
Phys::Quantity.new(self,nil,unit)
|
14
|
-
else
|
15
|
-
method_missing_orig(method,*args, &block)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
3
|
+
# @visibility private
|
4
|
+
class Numeric; include Phys::UnitsNumericMixin; end
|
5
|
+
include Phys::UnitsMixin
|
data/lib/phys/units/parse.rb
CHANGED
data/lib/phys/units/parse.y
CHANGED
data/lib/phys/units/quantity.rb
CHANGED
@@ -14,19 +14,27 @@ module Phys
|
|
14
14
|
# Quantity.new(*a)
|
15
15
|
#end
|
16
16
|
|
17
|
-
# Phys::Quantity is
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
17
|
+
# Phys::Quantity is the primary class of Phys-Units library,
|
18
|
+
# intended to be manipulated by users.
|
19
|
+
# This class represents Physical Quantities with a Unit of measurement.
|
20
|
+
# It contains *Value* and *Unit*.
|
21
|
+
# * *Value* of the quantity
|
22
|
+
# must be an instance having arithmetic methods,
|
22
23
|
# but it is not necessary to be a Numeric.
|
23
24
|
# This is a duck typing way.
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
25
|
+
# Phys::Quantity[2.5,"miles"].value #=> 2.5
|
26
|
+
# Phys::Quantity[NArray.float(5).indgen,"miles"].want("m").value
|
27
|
+
# #=> NArray.float(5):
|
28
|
+
# [ 0.0, 1609.34, 3218.69, 4828.03, 6437.38 ]
|
29
|
+
# * *Unit*
|
30
|
+
# is an instance of Phys::Unit class obtained by parsing *expr* string.
|
31
|
+
# see document of Phys::Unit.
|
32
|
+
# Phys::Quantity[2.5,"miles"].unit #=> #<Phys::Unit 1609.344,{"m"=>1},@expr="5280 ft">
|
33
|
+
#
|
34
|
+
# @example
|
28
35
|
# require 'phys/units'
|
29
|
-
# Q=Phys::Quantity
|
36
|
+
# Q = Phys::Quantity
|
37
|
+
#
|
30
38
|
# Q[1.23,'km'] + Q[4.56,'m'] #=> Phys::Quantity[1.23456,'km']
|
31
39
|
# Q[123,'mile'] / Q[2,'hr'] #=> Phys::Quantity[61,'mile/hr']
|
32
40
|
# Q[61,'miles/hr'].want('m/s') #=> Phys::Quantity[27.26944,'m/s']
|
@@ -34,43 +42,71 @@ module Phys
|
|
34
42
|
# Q[70,'tempF'] + Q[10,'tempC'] #=> Phys::Quantity[88,'tempF']
|
35
43
|
# Q[20,'tempC'].want('tempF') #=> Phys::Quantity[68,'tempF']
|
36
44
|
# Math.cos(Q[60,'degree'].to_f) #=> 0.5
|
45
|
+
#
|
46
|
+
# @see Unit
|
47
|
+
#
|
37
48
|
class Quantity
|
38
49
|
|
50
|
+
@@verbose_inspect = false
|
51
|
+
|
39
52
|
class << self
|
40
53
|
# Alias to Phys::Quantity.new.
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# @
|
44
|
-
#
|
54
|
+
# @param [Object] value Value of quantity.
|
55
|
+
# @param [String,Symbol] expr Unit string to be parsed later.
|
56
|
+
# @overload initialize(value,unit)
|
57
|
+
# @param [Object] value Value of quantity.
|
58
|
+
# @param [Phys::Unit] unit This unit is copyed if exists.
|
59
|
+
# @overload initialize(value)
|
60
|
+
# @param [Object] value Value of dimensionless quantity.
|
45
61
|
# @return [Phys::Quantity]
|
46
|
-
# @raise
|
62
|
+
# @raise [TypeError] if invalid arg types.
|
63
|
+
# @raise [Phys::UnitError] if unit conversion is failed.
|
47
64
|
def [](value,expr=nil)
|
48
65
|
self.new(value,expr)
|
49
66
|
end
|
50
67
|
end
|
51
68
|
|
52
69
|
# Initialize a new quantity.
|
53
|
-
# @
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# @
|
58
|
-
#
|
70
|
+
# @overload initialize(value,expr,unit=nil)
|
71
|
+
# @param [Object] value Value of quantity.
|
72
|
+
# @param [String,Symbol] expr Unit string to be parsed later.
|
73
|
+
# @param [Phys::Unit] unit This unit is copyed if exists.
|
74
|
+
# @overload initialize(value,unit)
|
75
|
+
# @param [Object] value Value of quantity.
|
76
|
+
# @param [Phys::Unit] unit This unit is copyed if exists.
|
77
|
+
# @overload initialize(value)
|
78
|
+
# @param [Object] value Value of dimensionless quantity.
|
79
|
+
# @raise [TypeError] if invalid arg types.
|
80
|
+
# @raise [Phys::UnitError] if unit conversion is failed.
|
59
81
|
#
|
60
82
|
def initialize(value,expr=nil,unit=nil)
|
61
83
|
@value = value
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
84
|
+
case expr
|
85
|
+
when String, Symbol
|
86
|
+
@expr = expr.to_s.strip
|
87
|
+
@expr = nil if @expr==''
|
88
|
+
@unit = unit
|
89
|
+
when NilClass
|
90
|
+
@expr = nil
|
91
|
+
@unit = unit
|
92
|
+
when Unit
|
93
|
+
raise "Wrong # of argument" if unit
|
94
|
+
@expr = nil
|
95
|
+
@unit = expr
|
96
|
+
else
|
97
|
+
raise ArgumentError,"Second argument is invalid: #{expr.inspect}"
|
98
|
+
end
|
99
|
+
case @unit
|
100
|
+
when NilClass
|
66
101
|
@unit = Unit.parse(@expr||1)
|
67
|
-
|
68
|
-
|
102
|
+
when Unit
|
103
|
+
else
|
104
|
+
raise ArgumentError, "Third argument is invalid: #{@unit.inspect}"
|
69
105
|
end
|
70
106
|
end
|
71
107
|
|
72
108
|
# Value of the quantity.
|
73
|
-
# Instance of classes with same arithmetic methods as
|
109
|
+
# Instance of classes with same arithmetic methods as Numeric.
|
74
110
|
# @return [Object]
|
75
111
|
attr_reader :value
|
76
112
|
alias val value
|
@@ -84,13 +120,28 @@ module Phys
|
|
84
120
|
attr_reader :unit
|
85
121
|
|
86
122
|
# Conversion to a quantity in another unit.
|
87
|
-
# @param [String]
|
88
|
-
# @return [Phys::Quantity] quantity in the unit of +
|
123
|
+
# @param [String,Symbol,Unit,Quantity] unit unit expression.
|
124
|
+
# @return [Phys::Quantity] quantity in the unit of +unit+.
|
89
125
|
# @raise [Phys::UnitError] if unit conversion is failed.
|
90
126
|
#
|
91
|
-
def want(
|
92
|
-
unit
|
93
|
-
|
127
|
+
def want(unit=nil)
|
128
|
+
case unit
|
129
|
+
when Unit
|
130
|
+
expr = unit.expr
|
131
|
+
when Quantity
|
132
|
+
expr = unit.expr
|
133
|
+
unit = unit.unit
|
134
|
+
when String,Symbol
|
135
|
+
expr = unit
|
136
|
+
unit = Unit.parse(expr)
|
137
|
+
when Numeric
|
138
|
+
unit = Unit.cast(unit)
|
139
|
+
when NilClass
|
140
|
+
unit = Unit.cast(1)
|
141
|
+
else
|
142
|
+
raise TypeError, "invalid argument: #{unit.inspect}"
|
143
|
+
end
|
144
|
+
val = unit.convert(self)
|
94
145
|
self.class.new( val, expr, unit )
|
95
146
|
end
|
96
147
|
alias convert want
|
@@ -204,7 +255,7 @@ module Phys
|
|
204
255
|
end
|
205
256
|
end
|
206
257
|
|
207
|
-
# Comparison. Returns +true+ if +self+ is
|
258
|
+
# Comparison. Returns +true+ if +self+ is greater-than or equal-to +other+.
|
208
259
|
# Before the comparison, it converts +other+ to the unit of +self+.
|
209
260
|
# @param [Phys::Quantity] other
|
210
261
|
# @return [Boolean]
|
@@ -260,7 +311,7 @@ module Phys
|
|
260
311
|
def **(n)
|
261
312
|
if @expr.nil?
|
262
313
|
expr = nil
|
263
|
-
elsif /^[A-Za-z_]
|
314
|
+
elsif /^[A-Za-z_]+$/o =~ @expr
|
264
315
|
expr = @expr+'^'+n.to_s
|
265
316
|
else
|
266
317
|
expr = '('+@expr+')^'+n.to_s+''
|
@@ -496,7 +547,32 @@ module Phys
|
|
496
547
|
else
|
497
548
|
expr = ""
|
498
549
|
end
|
499
|
-
|
550
|
+
if @@verbose_inspect
|
551
|
+
sufx = " "+@unit.inspect
|
552
|
+
else
|
553
|
+
sufx = ""
|
554
|
+
end
|
555
|
+
self.class.to_s+"["+Unit::Utils.num_inspect(@value)+expr+"]"+sufx
|
556
|
+
end
|
557
|
+
|
558
|
+
# Comformability of quantity. Returns true if unit conversion between +self+ and +x+ is possible.
|
559
|
+
# @param [Object] x other object (Unit or Quantity or Numeric or something else)
|
560
|
+
# @return [Boolean]
|
561
|
+
def ===(x)
|
562
|
+
case x
|
563
|
+
when Unit
|
564
|
+
unit === x
|
565
|
+
when Quantity
|
566
|
+
unit === x.unit
|
567
|
+
when Numeric
|
568
|
+
unit.dimensionless?
|
569
|
+
else
|
570
|
+
false
|
571
|
+
end
|
500
572
|
end
|
573
|
+
alias conformable? ===
|
574
|
+
alias compatible? ===
|
575
|
+
alias conversion_allowed? ===
|
576
|
+
|
501
577
|
end
|
502
578
|
end
|
data/lib/phys/units/unit.rb
CHANGED
@@ -9,22 +9,24 @@
|
|
9
9
|
|
10
10
|
module Phys
|
11
11
|
|
12
|
-
# Phys::Unit is a class to represent Physical
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# * *
|
17
|
-
#
|
18
|
-
#
|
12
|
+
# Phys::Unit is a class to represent Physical Units of measurement.
|
13
|
+
# This class is used in the Phys::Quantity for calculations with Units,
|
14
|
+
# and users do not always need to know its mechanism.
|
15
|
+
# It has *Factor* and *Dimension*:
|
16
|
+
# * *Factor* of the unit is a scale factor relative to its base unit.
|
17
|
+
# Phys::Unit["km"].factor #=> 1000
|
18
|
+
# * *Dimension* of the unit
|
19
|
+
# is a hash table containing base units and dimensions as key-value pairs.
|
19
20
|
# Phys::Unit["N"].dimension #=> {"kg"=>1, "m"=>1, "s"=>-2}
|
20
|
-
|
21
|
+
# @example
|
21
22
|
# require "phys/units"
|
22
23
|
# Q = Phys::Quantity
|
23
24
|
# U = Phys::Unit
|
24
25
|
#
|
25
|
-
# U["miles"] / U["hr"]
|
26
|
-
# U["hr"] + U["30 min"]
|
27
|
-
# U["(m/s)"]**2
|
26
|
+
# U["miles"] / U["hr"] #=> #<Phys::Unit 0.44704,{"m"=>1, "s"=>-1}>
|
27
|
+
# U["hr"] + U["30 min"] #=> #<Phys::Unit 5400,{"s"=>1}>
|
28
|
+
# U["(m/s)"]**2 #=> #<Phys::Unit 1,{"m"=>2, "s"=>-2}>
|
29
|
+
# U["m/s"] === Q[1,'miles/hr'] #=> true
|
28
30
|
#
|
29
31
|
# case Q[1,"miles/hr"]
|
30
32
|
# when U["m"]
|
@@ -36,6 +38,9 @@ module Phys
|
|
36
38
|
# else
|
37
39
|
# "other"
|
38
40
|
# end #=> "velocity"
|
41
|
+
#
|
42
|
+
# @see Quantity
|
43
|
+
#
|
39
44
|
class Unit
|
40
45
|
|
41
46
|
# @visibility private
|
@@ -50,7 +55,7 @@ module Phys
|
|
50
55
|
|
51
56
|
# Initialize a new unit.
|
52
57
|
# @overload initialize(factor,dimension=nil)
|
53
|
-
# @param [Numeric] factor Unit
|
58
|
+
# @param [Numeric] factor Unit scale factor.
|
54
59
|
# @param [Hash] dimension Dimension hash.
|
55
60
|
# @overload initialize(expr,name=nil)
|
56
61
|
# @param [String] expr Unit string to be parsed later.
|
@@ -58,23 +63,23 @@ module Phys
|
|
58
63
|
# @overload initialize(unit,name=nil)
|
59
64
|
# @param [Phys::Unit] unit Copy contents from the argument.
|
60
65
|
# @param [String] name Name of this unit.
|
61
|
-
# @raise [TypeError] if
|
66
|
+
# @raise [TypeError] if invalid arg types.
|
62
67
|
#
|
63
|
-
def initialize(
|
64
|
-
case
|
68
|
+
def initialize(a1,a2=nil)
|
69
|
+
case a1
|
65
70
|
when Numeric
|
66
|
-
|
67
|
-
@factor =
|
68
|
-
alloc_dim(
|
71
|
+
a1 = Rational(a1) if Integer===a1
|
72
|
+
@factor = a1
|
73
|
+
alloc_dim(a2)
|
69
74
|
when Phys::Unit
|
70
|
-
@factor =
|
71
|
-
alloc_dim
|
72
|
-
@name =
|
75
|
+
@factor = a1.factor
|
76
|
+
alloc_dim a1.dim
|
77
|
+
@name = a2.strip if a2
|
73
78
|
when String
|
74
|
-
@expr =
|
75
|
-
@name =
|
79
|
+
@expr = a1.strip
|
80
|
+
@name = a2.strip if a2
|
76
81
|
else
|
77
|
-
raise TypeError,"
|
82
|
+
raise TypeError,"Invalid argument: #{a1.inspect}"
|
78
83
|
end
|
79
84
|
end
|
80
85
|
|
@@ -97,16 +102,21 @@ module Phys
|
|
97
102
|
end
|
98
103
|
alias dim dimension
|
99
104
|
|
100
|
-
#
|
105
|
+
# Scale factor excluding the dimension-value.
|
101
106
|
# @return [Numeric]
|
107
|
+
# @example
|
108
|
+
# Phys::Unit["deg"].dimension #=> {"pi"=>1, "radian"=>1}
|
109
|
+
# Phys::Unit["deg"].factor #=> (1/180)
|
110
|
+
# Phys::Unit["deg"].conversion_factor #=> 0.017453292519943295
|
111
|
+
# @see #conversion_factor
|
102
112
|
def factor
|
103
113
|
use_dimension
|
104
114
|
@factor
|
105
115
|
end
|
106
116
|
|
107
|
-
# Dimension value.
|
108
|
-
# otherwise returns one. see BaseUnit.
|
117
|
+
# Dimension value. Always returns 1 unless BaseUnit.
|
109
118
|
# @return [Numeric]
|
119
|
+
# @see BaseUnit#dimension_value
|
110
120
|
def dimension_value
|
111
121
|
1
|
112
122
|
end
|
@@ -154,9 +164,12 @@ module Phys
|
|
154
164
|
|
155
165
|
# Inspect string.
|
156
166
|
# @return [String]
|
167
|
+
# @example
|
168
|
+
# Phys::Unit["N"].inspect #=> '#<Phys::Unit 1,{"kg"=>1, "m"=>1, "s"=>-2},@expr="newton">'
|
157
169
|
def inspect
|
170
|
+
use_dimension
|
158
171
|
a = [Utils.num_inspect(@factor), @dim.inspect]
|
159
|
-
|
172
|
+
a << "@name="+@name.inspect if @name
|
160
173
|
a << "@expr="+@expr.inspect if @expr
|
161
174
|
a << "@offset="+@offset.inspect if @offset
|
162
175
|
a << "@dimensionless=true" if @dimensionless
|
@@ -167,8 +180,10 @@ module Phys
|
|
167
180
|
"#<#{self.class} #{s}>"
|
168
181
|
end
|
169
182
|
|
170
|
-
# Make
|
183
|
+
# Make a string of this unit expressed in base units.
|
171
184
|
# @return [String]
|
185
|
+
# @example
|
186
|
+
# Phys::Unit["psi"].string_form #=> "(8896443230521/129032)*1e-04 kg m^-1 s^-2"
|
172
187
|
def unit_string
|
173
188
|
use_dimension
|
174
189
|
a = []
|
@@ -184,8 +199,13 @@ module Phys
|
|
184
199
|
end
|
185
200
|
alias string_form unit_string
|
186
201
|
|
187
|
-
# Conversion Factor to base unit.
|
202
|
+
# Conversion Factor to base unit, including dimension-value.
|
188
203
|
# @return [Numeric]
|
204
|
+
# @example
|
205
|
+
# Phys::Unit["deg"].dimension #=> {"pi"=>1, "radian"=>1}
|
206
|
+
# Phys::Unit["deg"].factor #=> (1/180)
|
207
|
+
# Phys::Unit["deg"].conversion_factor #=> 0.017453292519943295
|
208
|
+
# @see #factor
|
189
209
|
def conversion_factor
|
190
210
|
use_dimension
|
191
211
|
f = @factor
|
@@ -202,7 +222,7 @@ module Phys
|
|
202
222
|
|
203
223
|
# Returns true if scalar unit.
|
204
224
|
# *Scalar* means the unit does not have any dimension
|
205
|
-
# including dimensionless-
|
225
|
+
# including dimensionless-units, and its factor is one.
|
206
226
|
# @return [Boolean]
|
207
227
|
def scalar?
|
208
228
|
use_dimension
|
@@ -248,10 +268,10 @@ module Phys
|
|
248
268
|
end
|
249
269
|
end
|
250
270
|
|
251
|
-
# Comformability of units. Returns true if unit conversion is
|
252
|
-
# @param [Object] x other object (
|
271
|
+
# Comformability of units. Returns true if unit conversion between +self+ and +x+ is possible.
|
272
|
+
# @param [Object] x other object (Unit or Quantity or Numeric or something else)
|
253
273
|
# @return [Boolean]
|
254
|
-
def
|
274
|
+
def ===(x)
|
255
275
|
case x
|
256
276
|
when Unit
|
257
277
|
dimensionless_deleted == x.dimensionless_deleted
|
@@ -263,9 +283,9 @@ module Phys
|
|
263
283
|
false
|
264
284
|
end
|
265
285
|
end
|
266
|
-
alias
|
267
|
-
alias compatible?
|
268
|
-
alias conversion_allowed?
|
286
|
+
alias conformable? ===
|
287
|
+
alias compatible? ===
|
288
|
+
alias conversion_allowed? ===
|
269
289
|
|
270
290
|
# Convert a quantity to this unit.
|
271
291
|
# @param [Phys::Quantity] quantity to be converted.
|
@@ -314,7 +334,7 @@ module Phys
|
|
314
334
|
alias to_num to_numeric
|
315
335
|
|
316
336
|
|
317
|
-
# Returns Base Unit excluding dimensionless-
|
337
|
+
# Returns Base Unit excluding dimensionless-units.
|
318
338
|
# @return [Phys::Unit]
|
319
339
|
def base_unit
|
320
340
|
Unit.new(1,dimensionless_deleted)
|
@@ -367,7 +387,7 @@ module Phys
|
|
367
387
|
x.dup
|
368
388
|
end
|
369
389
|
else
|
370
|
-
raise "
|
390
|
+
raise "dimension not defined"
|
371
391
|
end
|
372
392
|
end
|
373
393
|
|
@@ -384,7 +404,7 @@ module Phys
|
|
384
404
|
end
|
385
405
|
dims
|
386
406
|
else
|
387
|
-
raise "
|
407
|
+
raise "dimension not defined"
|
388
408
|
end
|
389
409
|
end
|
390
410
|
|
@@ -465,29 +485,6 @@ module Phys
|
|
465
485
|
Unit.new(factor,dims)
|
466
486
|
end
|
467
487
|
|
468
|
-
# Rational division of units.
|
469
|
-
# Both units must be operable.
|
470
|
-
# @param [Phys::Unit, Numeric] x other unit
|
471
|
-
# @return [Phys::Unit]
|
472
|
-
# @raise [Phys::UnitError] if not operable.
|
473
|
-
def rdiv(x)
|
474
|
-
x = Unit.cast(x)
|
475
|
-
if scalar?
|
476
|
-
return x.inverse
|
477
|
-
elsif x.scalar?
|
478
|
-
return self
|
479
|
-
end
|
480
|
-
check_operable2(x)
|
481
|
-
dims = dimension_binop(x){|a,b| a-b}
|
482
|
-
factor = Rational(self.factor,x.factor)
|
483
|
-
Unit.new(factor,dims)
|
484
|
-
end
|
485
|
-
|
486
|
-
# @visibility private
|
487
|
-
def self.rdiv(x,y)
|
488
|
-
Unit.cast(x).rdiv(y)
|
489
|
-
end
|
490
|
-
|
491
488
|
# Inverse of units.
|
492
489
|
# This unit must be operable.
|
493
490
|
# @param [Phys::Unit, Numeric] unit
|
@@ -548,26 +545,32 @@ module Phys
|
|
548
545
|
end # Unit
|
549
546
|
|
550
547
|
|
551
|
-
# BaseUnit is a class to represent units defined by "!"
|
552
|
-
#
|
548
|
+
# BaseUnit is a class to represent units defined by "!"
|
549
|
+
# in the data form of GNU units.
|
550
|
+
# It includes SI units and dimensinless units such as radian.
|
553
551
|
class BaseUnit < Unit
|
554
552
|
|
555
553
|
def self.define(name,expr,dimval=nil)
|
556
|
-
|
557
|
-
|
554
|
+
if LIST[name]
|
555
|
+
warn "unit definition is overwritten: #{name}" if debug
|
556
|
+
end
|
557
|
+
LIST[name] = self.new(expr,name,dimval)
|
558
558
|
end
|
559
559
|
|
560
|
-
def initialize(name,
|
560
|
+
def initialize(expr,name,dimval=nil)
|
561
561
|
case name
|
562
562
|
when String
|
563
|
-
@name = name
|
563
|
+
@name = name.strip
|
564
564
|
@factor = 1
|
565
|
-
@dim = {name=>1}
|
565
|
+
@dim = {@name=>1}
|
566
566
|
@dim.default = 0
|
567
|
-
@
|
567
|
+
@expr = expr
|
568
|
+
if String===expr && /!dimensionless/ =~ expr
|
569
|
+
@dimensionless = true
|
570
|
+
end
|
568
571
|
@dimension_value = dimval || 1
|
569
572
|
else
|
570
|
-
raise ArgumentError
|
573
|
+
raise ArgumentError,"Second argument must be string: #{name}"
|
571
574
|
end
|
572
575
|
end
|
573
576
|
|
@@ -598,7 +601,7 @@ module Phys
|
|
598
601
|
|
599
602
|
|
600
603
|
# OffsetUnit is a class to represent units with offset value.
|
601
|
-
#
|
604
|
+
# Allows Farenheight/Celsius temperature.
|
602
605
|
class OffsetUnit < Unit
|
603
606
|
|
604
607
|
def self.define(name,unit,offset=nil)
|
@@ -623,7 +626,7 @@ module Phys
|
|
623
626
|
v = quantity.value * quantity.unit.conversion_factor
|
624
627
|
v = v / self.conversion_factor
|
625
628
|
else
|
626
|
-
raise UnitError,"not
|
629
|
+
raise UnitError,"not Quantity: #{quantity.inspect}"
|
627
630
|
end
|
628
631
|
end
|
629
632
|
|
@@ -18,12 +18,16 @@ module Phys
|
|
18
18
|
false
|
19
19
|
end
|
20
20
|
|
21
|
-
# Define
|
22
|
-
# @param [String] name Name of this unit.
|
21
|
+
# Define a new Unit. Expression is not parsed at the time of this method.
|
22
|
+
# @param [String,Symbol] name Name of this unit.
|
23
23
|
# @param [String] expr Expression.
|
24
24
|
def define(name,expr)
|
25
|
-
|
26
|
-
|
25
|
+
case name
|
26
|
+
when String
|
27
|
+
when Symbol
|
28
|
+
name = name.to_s
|
29
|
+
else
|
30
|
+
raise TypeError,"Unit name must be String or Symbol: #{name.inspect}"
|
27
31
|
end
|
28
32
|
if /^(.*)-$/ =~ name
|
29
33
|
name = $1
|
@@ -36,8 +40,7 @@ module Phys
|
|
36
40
|
warn "unit definition is overwritten: #{name}" if debug
|
37
41
|
end
|
38
42
|
if expr.kind_of?(String) && /^!/ =~ expr
|
39
|
-
|
40
|
-
LIST[name] = BaseUnit.new(name,dimless)
|
43
|
+
LIST[name] = BaseUnit.new(expr,name)
|
41
44
|
else
|
42
45
|
LIST[name] = self.new(expr,name)
|
43
46
|
end
|
@@ -45,10 +48,12 @@ module Phys
|
|
45
48
|
end
|
46
49
|
|
47
50
|
|
48
|
-
# Force argument to be Phys::Unit.
|
51
|
+
# Force the argument to be Phys::Unit.
|
52
|
+
# @param [Phys::Unit,Numeric]
|
49
53
|
# @return [Phys::Unit]
|
50
54
|
def cast(x)
|
51
|
-
|
55
|
+
case x
|
56
|
+
when Unit
|
52
57
|
x
|
53
58
|
else
|
54
59
|
Unit.new(x)
|
@@ -61,8 +66,9 @@ module Phys
|
|
61
66
|
find_unit(x) or raise UnitError, "Undefined unit: #{x.inspect}"
|
62
67
|
end
|
63
68
|
|
64
|
-
# Searches a registered unit and then
|
69
|
+
# Searches a registered unit and then parse as a unit string
|
65
70
|
# if not registered.
|
71
|
+
# @param [String,Symbol,Numeric,Unit,Quantity,NilClass] x
|
66
72
|
# @return [Phys::Unit]
|
67
73
|
def parse(x)
|
68
74
|
find_unit(x) || Unit.cast(Parse.new.parse(x))
|
@@ -70,15 +76,27 @@ module Phys
|
|
70
76
|
alias [] parse
|
71
77
|
|
72
78
|
# Searches a registered unit.
|
79
|
+
# @param [String,Symbol,Numeric,Unit,Quantity,NilClass] x
|
73
80
|
# @return [Phys::Unit, NilClass]
|
74
81
|
def find_unit(x)
|
75
|
-
|
82
|
+
case x
|
83
|
+
when String,Symbol
|
84
|
+
x = x.to_s.strip
|
85
|
+
if x==''
|
86
|
+
Unit.new(1)
|
87
|
+
else
|
88
|
+
LIST[x] || PREFIX[x] || find_prefix(x) || unit_stem(x)
|
89
|
+
end
|
90
|
+
when Numeric
|
76
91
|
Unit.new(x)
|
77
|
-
|
92
|
+
when NilClass
|
78
93
|
Unit.new(1)
|
94
|
+
when Unit
|
95
|
+
x
|
96
|
+
when Quantity
|
97
|
+
x.unit
|
79
98
|
else
|
80
|
-
|
81
|
-
LIST[x] || PREFIX[x] || find_prefix(x) || unit_stem(x)
|
99
|
+
raise TypeError, "Invalid argument: #{x.inspect}"
|
82
100
|
end
|
83
101
|
end
|
84
102
|
|
@@ -165,8 +183,7 @@ module Phys
|
|
165
183
|
end
|
166
184
|
|
167
185
|
if /^([^\s()\[\]{}!*|\/^#]+)\s+([^#]+)/ =~ str
|
168
|
-
|
169
|
-
Unit.define(name,repr)
|
186
|
+
Unit.define($1,$2.strip)
|
170
187
|
elsif !str.strip.empty?
|
171
188
|
puts "unrecognized definition: '#{str}'" if debug
|
172
189
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Phys
|
2
|
+
|
3
|
+
# @visibility private
|
4
|
+
module UnitMeasures
|
5
|
+
def self.included(mod)
|
6
|
+
Phys::Unit::LIST.each do |k,u|
|
7
|
+
if /^[A-Z]\w*$/ =~ k
|
8
|
+
const_set(k,u)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Defines method with unit name.
|
15
|
+
# *Caution*: Variable names may conflict with unit names.
|
16
|
+
# @example
|
17
|
+
# Phys::UnitsMixin.module_eval do
|
18
|
+
# puts 123.4*km
|
19
|
+
# puts (23*mile/hr).want(m/s)
|
20
|
+
# puts h.to_si
|
21
|
+
# case mile/hr
|
22
|
+
# when m
|
23
|
+
# puts "length"
|
24
|
+
# when m/s
|
25
|
+
# puts "velocity"
|
26
|
+
# else
|
27
|
+
# puts "other"
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# include Phys::UnitsMixin
|
32
|
+
#
|
33
|
+
# (1*miles/hr).want m/s #=> Phys::Quantity[0.44704,"m/s"]
|
34
|
+
#
|
35
|
+
module UnitsMixin
|
36
|
+
include UnitMeasures
|
37
|
+
module_function
|
38
|
+
alias method_missing_units_alias method_missing
|
39
|
+
def method_missing(method, *args, &block)
|
40
|
+
if unit=Phys::Unit.find_unit(method)
|
41
|
+
raise "argument must be empty" unless args.empty?
|
42
|
+
Phys::Quantity.new(1,method,unit)
|
43
|
+
else
|
44
|
+
method_missing_units_alias(method, *args, &block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def print_units(unit=nil)
|
49
|
+
Phys::Unit::LIST.each do |k,u|
|
50
|
+
if unit.nil? || unit===u
|
51
|
+
len = 32 - k.size
|
52
|
+
len = 1 if len < 1
|
53
|
+
puts k+" "*len+"#{u.expr}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# ActiveSupport-like mix-in.
|
61
|
+
# *Caution*: This kind of global change will cause unexpected problems.
|
62
|
+
# @example
|
63
|
+
# class Numeric
|
64
|
+
# include Phys::UnitsNumericMixin
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# (1.miles/1.hr).want 'm/s' #=> Phys::Quantity[0.44704,"m/s"]
|
68
|
+
module UnitsNumericMixin
|
69
|
+
alias method_missing_units_alias method_missing
|
70
|
+
def method_missing(method, *args, &block)
|
71
|
+
if unit=Phys::Unit.find_unit(method)
|
72
|
+
raise "argument must be empty" unless args.empty?
|
73
|
+
Phys::Quantity.new(self,method,unit)
|
74
|
+
else
|
75
|
+
method_missing_units_alias(method, *args, &block)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
data/lib/phys/units/version.rb
CHANGED
data/spec/unit_spec.rb
CHANGED
@@ -38,7 +38,7 @@ describe "Create Units" do
|
|
38
38
|
its(:factor) {should == 1}
|
39
39
|
its(:conversion_factor) {should == Math::PI}
|
40
40
|
its(:name) {should == 'pi'}
|
41
|
-
its(:expr) {should
|
41
|
+
its(:expr) {should == '!dimensionless'}
|
42
42
|
its(:offset) {should be_nil}
|
43
43
|
its(:dimension) {should == {'pi'=>1}}
|
44
44
|
its(:dimension_value) {should == Math::PI}
|
@@ -53,7 +53,7 @@ describe "Create Units" do
|
|
53
53
|
its(:factor) {should == 1}
|
54
54
|
its(:conversion_factor) {should == 1}
|
55
55
|
its(:name) {should == 'm'}
|
56
|
-
its(:expr) {should
|
56
|
+
its(:expr) {should == '!'}
|
57
57
|
its(:offset) {should be_nil}
|
58
58
|
its(:dimension) {should == {'m'=>1}}
|
59
59
|
its(:dimension_value) {should == 1}
|
@@ -173,6 +173,7 @@ describe "Create Units" do
|
|
173
173
|
it {should_not be_dimensionless}
|
174
174
|
it {should_not be_scalar}
|
175
175
|
it {should be_operable}
|
176
|
+
it {should === Q[1,'miles/hr']}
|
176
177
|
end
|
177
178
|
|
178
179
|
describe U.parse('(m/s)**2') do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phys-units
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masahiro TANAKA
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- lib/phys/units/quanty.rb
|
63
63
|
- lib/phys/units/unit.rb
|
64
64
|
- lib/phys/units/unit_class.rb
|
65
|
+
- lib/phys/units/units_mixin.rb
|
65
66
|
- lib/phys/units/utils.rb
|
66
67
|
- lib/phys/units/version.rb
|
67
68
|
- misc/mkjpspec.rb
|