pulo 0.1.1 → 0.1.2

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.
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