pulo 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pulo.rb +28 -0
  3. data/lib/pulo/exceptions.rb +6 -0
  4. data/lib/pulo/figure/figure2d.rb +248 -0
  5. data/lib/pulo/figure/figure3d.rb +166 -0
  6. data/lib/pulo/formatting.rb +140 -0
  7. data/lib/pulo/frames/frame.rb +289 -0
  8. data/lib/pulo/frames/frame_cell.rb +83 -0
  9. data/lib/pulo/frames/frame_column.rb +149 -0
  10. data/lib/pulo/frames/frame_row.rb +87 -0
  11. data/lib/pulo/helpers.rb +1 -0
  12. data/lib/pulo/machine/hydraulics/pipe.rb +71 -0
  13. data/lib/pulo/machine/machines.rb +10 -0
  14. data/lib/pulo/machine/mechanics/moments_of_inertia.rb +64 -0
  15. data/lib/pulo/machine/steam/boiler.rb +61 -0
  16. data/lib/pulo/machine/steam/boiler_deaerator.rb +64 -0
  17. data/lib/pulo/machine/steam/deaerator.rb +37 -0
  18. data/lib/pulo/machine/steam/desuperheater.rb +29 -0
  19. data/lib/pulo/machine/steam/header.rb +20 -0
  20. data/lib/pulo/machine/steam/if97.rb +378 -0
  21. data/lib/pulo/machine/steam/steam_process.rb +27 -0
  22. data/lib/pulo/machine/steam/steam_turbine.rb +42 -0
  23. data/lib/pulo/machine/steam/water_steam.rb +229 -0
  24. data/lib/pulo/material/water.rb +34 -0
  25. data/lib/pulo/quantity/dimension.rb +63 -0
  26. data/lib/pulo/quantity/numeric_overloads.rb +217 -0
  27. data/lib/pulo/quantity/quantity.rb +285 -0
  28. data/lib/pulo/quantity/quantity_builder.rb +185 -0
  29. data/lib/pulo/quantity/quantity_definitions.rb +8 -0
  30. data/lib/pulo/quantity/quantity_definitions/area_volume.rb +73 -0
  31. data/lib/pulo/quantity/quantity_definitions/basic.rb +157 -0
  32. data/lib/pulo/quantity/quantity_definitions/electric.rb +22 -0
  33. data/lib/pulo/quantity/quantity_definitions/energy.rb +50 -0
  34. data/lib/pulo/quantity/quantity_definitions/fluids.rb +23 -0
  35. data/lib/pulo/quantity/quantity_definitions/force_power.rb +82 -0
  36. data/lib/pulo/quantity/quantity_definitions/rotation.rb +49 -0
  37. data/lib/pulo/quantity/quantity_definitions/value.rb +65 -0
  38. data/lib/pulo/quantity/quantity_definitions/velocity_acc_flow.rb +66 -0
  39. data/lib/pulo/quantity/quantity_groups/quantity_groups.rb +36 -0
  40. data/lib/pulo/quantity/unit.rb +45 -0
  41. data/lib/pulo/quantity_checker.rb +13 -0
  42. data/lib/pulo/tables/density.rb +10 -0
  43. data/lib/pulo/tables/melting_temperature.rb +9 -0
  44. data/lib/pulo/tables/specific_energy.rb +10 -0
  45. data/lib/pulo/tables/speed_of_sound.rb +9 -0
  46. data/lib/pulo/tables/tables.rb +104 -0
  47. data/lib/pulo/tables/tensile_strength.rb +10 -0
  48. data/lib/pulo/tables/yield_strength.rb +10 -0
  49. data/lib/pulo/version.rb +3 -0
  50. metadata +51 -3
@@ -0,0 +1,285 @@
1
+ # encoding: utf-8
2
+
3
+ module Pulo
4
+ #Module instance variables to store global settings
5
+ class << self
6
+ attr_accessor :precision
7
+ attr_accessor :significant_figures
8
+ attr_accessor :supress_quantity_names
9
+
10
+ def quantities; @quantities||={};end #hash with dimension_spec as the key
11
+
12
+ def help
13
+ @quantities.each do |dim|
14
+
15
+ dim[1].each do |quan|
16
+ names=quan.quantity_name
17
+ if quan.synonyms.length>0
18
+ names += ', ' + quan.synonyms.join(', ')
19
+ end
20
+
21
+ puts names
22
+ puts '---------------------------------------------------------------------------------------'
23
+ puts '[' + dim[0].to_s + ']'
24
+ puts quan.units_sorted
25
+ puts ''
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ Pulo.precision = 2
32
+ Pulo.significant_figures = false
33
+ Pulo.supress_quantity_names = false
34
+
35
+
36
+ module Pulo
37
+ class Quantity
38
+ include Comparable
39
+ #=====================================
40
+ #Quantity: Class variables and methods - For child quantity classes
41
+ #=====================================
42
+ class << self
43
+ def units; @units ||={}; end
44
+ def si_unit_scales; @si_unit_scales ||={};end
45
+ def synonyms; @synonyms ||=[]; end
46
+
47
+ attr_accessor :base_unit
48
+ attr_accessor :dimensions
49
+
50
+ def best_si_unit(scale)
51
+ @si_unit_scales.min_by do |unit|
52
+ (scale-unit[0]).abs
53
+ end[1]
54
+ end
55
+
56
+ def method_missing(method_sym, *arguments, &block)
57
+ puts "#{quantity_name} doesn't have a unit #{method_sym}."
58
+ puts "Available units are: " + units.map{|unt| unt[1].name }.join(', ')
59
+ end
60
+
61
+ def quantity_name
62
+ self.name.split('::')[1]
63
+ end
64
+
65
+ def units_sorted
66
+ self.units.values.sort do |a,b|
67
+ next -1 if a.is_si? && !b.is_si?
68
+
69
+ next 1 if !a.is_si? && b.is_si?
70
+
71
+ if a.is_si?
72
+ next -1 if a.scale<b.scale
73
+ next 1
74
+ else
75
+ if a.si_convert_unit==b.si_convert_unit
76
+ next a.si_convert_factor<=>b.si_convert_factor
77
+ else
78
+ a.si_convert_unit<=>b.si_convert_unit
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ #=====================================
86
+ #Instance variables and methods - Child Quantities
87
+ #=====================================
88
+ attr_accessor :value, :unit
89
+
90
+ def initialize(value=nil, unit=nil)
91
+ value ||= 1.0
92
+ if unit
93
+ if unit.is_a?(Symbol)
94
+ raise "Unit #{unit.to_s} not defined for #{self.class.quantity_name}." unless self.class.units[unit]
95
+
96
+ self.unit=self.class.units[unit]
97
+ else
98
+ self.unit=unit
99
+ end
100
+ else
101
+ self.unit=self.class.base_unit
102
+ end
103
+ self.value=Float(value) unless self.value.is_a?(Float)
104
+ self
105
+ end
106
+
107
+ def dimensions
108
+ self.class.dimensions
109
+ end
110
+
111
+ def to_s(precision=nil, supress_quantity_names=false)
112
+ "#{self.class.quantity_name + ': ' unless Pulo.supress_quantity_names || supress_quantity_names || self.class==Dimensionless}#{NumberToRoundedConverter.convert(@value,precision)} #{@unit.abbreviation}"
113
+ end
114
+
115
+ def to; self; end
116
+ def in; self; end
117
+
118
+ #Pass unknown methods through to the underlying value (Float) if it responds. eg floor and modulo methods
119
+ def method_missing(method_sym, *arguments, &block)
120
+ if self.value.respond_to?(method_sym)
121
+ self.value.send(method_sym,*arguments,&block)
122
+ end
123
+ end
124
+
125
+ def inverse
126
+ Dimensionless.new/self
127
+ end
128
+ def -@
129
+ self.class.new -self.value,self.unit
130
+ end
131
+ def +(other)
132
+ case
133
+ when other.is_a?(Numeric)
134
+ self.class.new self.value+other,self.unit
135
+ when other.class.dimensions==self.class.dimensions
136
+ if self.unit==other.unit
137
+ self.class.new self.value+other.value,self.unit
138
+ else
139
+ self.class.new self.value+other.send(self.unit.name).value,self.unit
140
+ end
141
+ else
142
+ raise QuantitiesException.new("Cannot add a #{other.class.name} to a #{self.class.name}")
143
+ end
144
+ end
145
+ def -(other)
146
+ case
147
+ when other.is_a?(Numeric)
148
+ self.class.new self.value-other,self.unit
149
+ when other.class.dimensions==self.class.dimensions
150
+ if self.unit==other.unit
151
+ self.class.new self.value-other.value,self.unit
152
+ else
153
+ self.class.new self.value-other.send(self.unit.name).value,self.unit
154
+ end
155
+ else
156
+ raise QuantitiesException.new("Cannot minus a #{other.class.name} from a #{self.class.name}")
157
+ end
158
+ end
159
+ def *(other)
160
+ case
161
+ when other.is_a?(Numeric)
162
+ self.class.new self.value*other,self.unit
163
+ when other.is_a?(Quantity)
164
+ new_dims=self.class.dimensions+other.class.dimensions
165
+
166
+ #get both quantities to their equivalent SI if needed
167
+ q1=self; q1=q1.to_si unless q1.is_si?
168
+ q2=other; q2=q2.to_si unless q2.is_si?
169
+
170
+ target_scale=q1.unit.scale+q2.unit.scale
171
+ target_value=q1.value*q2.value
172
+ existing_or_new_quantity new_dims,target_scale,target_value
173
+ else
174
+ raise QuantitiesException.new("Cannot multiply a #{other.class.name} and a #{self.class.name}")
175
+ end
176
+ end
177
+ def /(other)
178
+ case
179
+ when other.is_a?(Numeric)
180
+ self.class.new self.value/other,self.unit
181
+ when other.is_a?(Quantity)
182
+ new_dims=self.class.dimensions-other.class.dimensions
183
+
184
+ q1=self; q1=q1.to_si unless q1.is_si?
185
+ q2=other; q2=q2.to_si unless q2.is_si?
186
+
187
+ target_scale=q1.unit.scale-q2.unit.scale
188
+ target_value=q1.value/q2.value
189
+ existing_or_new_quantity new_dims,target_scale,target_value
190
+ else
191
+ raise QuantitiesException.new("Cannot divide a #{self.class.name} by a #{other.class.name}")
192
+ end
193
+ end
194
+ def **(power)
195
+ raise QuantitiesException.new('Can only raise a quantity to an integer power') unless power.is_a?(Integer)
196
+
197
+ new_dims=self.class.dimensions*power
198
+ q1=self; q1=q1.to_si unless q1.is_si?
199
+
200
+ target_scale=q1.unit.scale*power
201
+ target_value=q1.value**power
202
+
203
+ existing_or_new_quantity new_dims,target_scale,target_value
204
+ end
205
+ def rt(power)
206
+ raise QuantitiesException.new('Can only do integer roots') unless power.is_a?(Integer)
207
+
208
+ self.class.dimensions.spec.each do |dim|
209
+ if dim[1]/power.to_f % 1 != 0
210
+ raise QuantitiesException.new('Root would lead to non-integer dimensions')
211
+ end
212
+ end
213
+ new_dims=self.class.dimensions/power
214
+
215
+ q1=self; q1=q1.to_si unless q1.is_si?
216
+
217
+ target_scale=q1.unit.scale/power
218
+ target_value=q1.value**(1.0/power)
219
+ existing_or_new_quantity new_dims,target_scale,target_value
220
+ end
221
+ def <=>(other)
222
+ unless (other.is_a?(Quantity) && self.class.dimensions==other.class.dimensions) || (other.is_a?(Numeric) && self.class==Dimensionless)
223
+ raise QuantitiesException.new("Can only compare quantities with same dimensions (given: #{self.class} and #{other.class}).")
224
+ end
225
+ if other.is_a?(Numeric)
226
+ other=Dimensionless.n(other)
227
+ end
228
+ to_base_unit.value<=>other.to_base_unit.value
229
+ end
230
+
231
+ def existing_or_new_quantity(new_dims, target_scale, target_value)
232
+ if Pulo.quantities[new_dims]
233
+ klass=Pulo.quantities[new_dims][0]
234
+ unit=klass.best_si_unit Math.log10(target_value.abs) + target_scale
235
+ klass.new(target_value*10**(target_scale-unit.scale), unit)
236
+ else
237
+ qname=new_dims.to_s(true).gsub(/-/, '_')
238
+ QuantityBuilder.build(qname) do
239
+ # noinspection RubyArgCount
240
+ dimensions new_dims.spec
241
+ si_unit '0.0'+qname, '', new_dims.to_s, 1.0
242
+ unless target_scale==0
243
+ si_unit target_scale.to_s+qname, '', new_dims.to_s+'*10^'+target_scale.to_s, 1.0*10**target_scale
244
+ end
245
+ end.klass.send(target_scale.to_s+qname, target_value)
246
+ end
247
+ end
248
+
249
+ def to_base_unit
250
+ self.send(self.class.base_unit.name)
251
+ end
252
+
253
+ def is_si?
254
+ @unit.is_si?
255
+ end
256
+
257
+ def to_si
258
+ return self if self.is_si?
259
+ self.send(self.unit.si_convert_unit)
260
+ end
261
+
262
+ #Converts to SI and most 'natural' scale
263
+ def rescale
264
+ unless self.is_si?
265
+ return self.to_base_unit.rescale
266
+ end
267
+ scale=Math.log10(self.value)+self.unit.scale
268
+
269
+ unit_scales=self.class.si_unit_scales.sort
270
+
271
+ if scale<unit_scales[0][0]
272
+ return self.send unit_scales[0][1].name
273
+ end
274
+ if scale>=unit_scales.last[0]
275
+ return self.send unit_scales.last[1].name
276
+ end
277
+ unit_scales.each_cons(2) do |us|
278
+ if us[0][0]<=scale && us[1][0]>scale
279
+ return self.send us[0][1].name
280
+ end
281
+ end
282
+ end
283
+
284
+ end
285
+ end
@@ -0,0 +1,185 @@
1
+ # encoding: utf-8
2
+
3
+ module Pulo
4
+ class QuantityBuilder
5
+
6
+ @unit_group_name
7
+
8
+ def self.build(name,&block)
9
+ klass=Class.new(Quantity)
10
+ Pulo.const_set(name,klass)
11
+ QuantityBuilder.new(klass,&block)
12
+ end
13
+
14
+ def initialize(klass,&block)
15
+ @klass=klass
16
+ instance_eval(&block)
17
+ end
18
+
19
+ def klass
20
+ @klass
21
+ end
22
+
23
+ private
24
+
25
+ def define_method_on_klass(klass,method, &block)
26
+ klass.send :define_method, method, &block
27
+ end
28
+
29
+ def dimensions(*dims)
30
+ #Create a dimensions object based on the supplied specification and set it on the new class
31
+ spec=Dimension.new(dims[0])
32
+ @klass.dimensions=spec
33
+
34
+ if Pulo.quantities[spec]
35
+ #If a quantity has already been defined with this set of dimensions
36
+
37
+ Pulo.quantities[spec].each do |quan_klass|
38
+ #for each of the other quantities with the same dimensions
39
+ # define a method on them with the name of this quantity
40
+ quan_klass.instance_exec(@klass) do |other_klass|
41
+ define_method other_klass.quantity_name.downcase do
42
+ other_unit=other_klass.si_unit_scales[self.unit.scale]
43
+ other_unit=other_klass.best_si_unit(Math.log10(self.value)+self.unit.scale) unless other_unit
44
+ return other_klass.new(self.value*10**(self.unit.scale-other_unit.scale),other_unit)
45
+ end
46
+ end
47
+
48
+ #and define a method on us for the reverse
49
+ define_method_on_klass @klass, quan_klass.quantity_name.downcase do
50
+ other_unit=quan_klass.si_unit_scales[self.unit.scale]
51
+ other_unit=quan_klass.best_si_unit(Math.log10(self.value)+self.unit.scale) unless other_unit
52
+ return quan_klass.new(self.value*10**(self.unit.scale-other_unit.scale),other_unit)
53
+ end
54
+ end
55
+ Pulo.quantities[spec] << @klass
56
+ else
57
+ Pulo.quantities.merge!({spec=>[@klass]})
58
+ end
59
+ end
60
+
61
+ def synonyms(*synonyms)
62
+ synonyms.each do |synonym|
63
+ @klass.synonyms << synonym
64
+ Pulo.const_set(synonym,@klass.clone)
65
+ end
66
+ end
67
+
68
+ def constant(name,symbol,unit,value)
69
+ @klass.define_singleton_method(name) do
70
+ # noinspection RubyArgCount
71
+ self.new value, self.units[unit]
72
+ end
73
+ unless symbol==''
74
+ @klass.define_singleton_method(symbol) do
75
+ # noinspection RubyArgCount
76
+ self.new value, self.units[unit]
77
+ end
78
+ end
79
+ end
80
+
81
+ def si_unit(name,plural,abbreviation,scale)
82
+ scale=Math.log10(scale)
83
+ unit=SI_Unit.new(name,plural, abbreviation,scale)
84
+ if scale==0
85
+ @klass.base_unit=unit
86
+ end
87
+ define_unit_methods unit
88
+ @klass.si_unit_scales.merge!({scale=>unit})
89
+ end
90
+
91
+ def non_si_unit(name,plural,abbreviation,si_convert_unit,si_convert_factor)
92
+ unit=NonSI_Unit.new(name,plural, abbreviation, si_convert_unit, si_convert_factor)
93
+ define_unit_methods unit
94
+ end
95
+
96
+ def define_unit_methods(unit)
97
+ @klass.units.merge!({unit.name=>unit})
98
+
99
+ #Class method for creating by unit name
100
+ @klass.define_singleton_method("#{unit.name}") do |val|
101
+ # noinspection RubyArgCount
102
+ self.new val,unit
103
+ end
104
+
105
+ #Class method for creating by unit plural name
106
+ unless unit.name.to_s==unit.plural
107
+ @klass.define_singleton_method("#{unit.plural}") do |val|
108
+ self.send(unit.name,val) #Could also be done as a method alias?
109
+ end
110
+ end
111
+
112
+ #Class method for creating by unit abbreviated name
113
+ unless unit.name.to_s==unit.abbreviation || unit.abbreviation==''
114
+ @klass.define_singleton_method("#{unit.abbreviation}") do |val|
115
+ self.send(unit.name,val) #Could also be done as a method alias?
116
+ end
117
+ end
118
+
119
+ #Instance method for conversion to this unit by plural
120
+ unless unit.name.to_s==unit.plural
121
+ define_method_on_klass(@klass,"#{unit.plural}") do
122
+ self.send(unit.name) #Could also be done as a method alias?
123
+ end
124
+ end
125
+
126
+ #Instance method for conversion to this unit by abbreviation
127
+ unless unit.name.to_s==unit.abbreviation || unit.abbreviation==''
128
+ define_method_on_klass(@klass,"#{unit.abbreviation}") do
129
+ self.send(unit.name) #Could also be done as a method alias?
130
+ end
131
+ end
132
+
133
+ define_method_on_klass(@klass,"#{unit.name}") do
134
+ #If its conversion to and from the same unit then just return self
135
+ if self.unit.name==unit.name ##TODO: Make == work for units
136
+ return self
137
+ end
138
+
139
+ #Actual conversions depending on si or non-si from and to units
140
+ case
141
+
142
+ #si to si
143
+ when self.unit.is_si? && unit.is_si?
144
+ # noinspection RubyArgCount
145
+ self.class.new self.value*10**(self.unit.scale-unit.scale),unit
146
+
147
+ #si to non-si
148
+ when self.unit.is_si? && !unit.is_si?
149
+ to_unit=self.class.units[unit.name]
150
+ if to_unit.si_convert_unit==self.unit.name
151
+ intermediate_si=self
152
+ else
153
+ intermediate_si=self.send(to_unit.si_convert_unit)
154
+ end
155
+ # noinspection RubyArgCount
156
+ self.class.new intermediate_si.value*to_unit.si_convert_factor,unit
157
+
158
+ #non-si to si
159
+ when !self.unit.is_si? && unit.is_si?
160
+ # noinspection RubyArgCount
161
+ intermediate_si=self.class.new self.value/self.unit.si_convert_factor,self.class.units[self.unit.si_convert_unit]
162
+ if intermediate_si.unit==unit
163
+ intermediate_si
164
+ else
165
+ intermediate_si.send(unit.name)
166
+ end
167
+
168
+ #non-si to non-si
169
+ else
170
+ #get the destination non-si unit definition
171
+ to_unit=self.class.units[unit.name]
172
+
173
+ #first convert self to si
174
+ intermediate_si=self.send(self.unit.si_convert_unit)
175
+
176
+ #then convert this si to the si 'partner' of the destination unit
177
+ intermediate_si2=intermediate_si.send(to_unit.si_convert_unit)
178
+
179
+ #lastly convert this to the final required non-si
180
+ intermediate_si2.send(to_unit.name)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end