Class: Quantify::Dimensions

Inherits:
Object
  • Object
show all
Defined in:
lib/quantify/dimensions.rb

Overview

The Dimensions class represents specfic physical quantities in terms of powers of their constituent base dimensions, e.g.:

area = length^2 force = mass^1 x length^1 x time^-2

Each dimension object is characterised by instance variables which describe the power (or index) of the respective base dimensions. Dimension objects can be manipulated - multiplied, divided, raised to powers, etc.

Standard physical quantities (e.g. length, acceleration, energy) are loaded into the @@dimensions class variable at runtime. These can be accessed, used and manipulated for arbitrary dimensional uses.

Instances of Dimensions are also used as the basis for defining and manipulating objects of the Unit::Base class.

Constant Summary collapse

BASE_QUANTITIES =

The BASE_QUANTITIES array specifies the system of base quantities upon which all Dimensions objects are defined.

:information, :currency, :item represent tentative additions to the standard set of base quantities.

:item is intended to represent arbitrary ‘things’ for specifying quantities such as, for example:

‘dollars per capita’ (:currency => 1, :items => -1) ‘trees per hectare’ (:items => 1, :length => -2).

[
:mass, :length, :time, :electric_current, :temperature,
:luminous_intensity, :amount_of_substance, :information,
:currency, :item ]
@@dimensions =

Class variable which holds in memory all defined (and ‘loaded’) quantities

[]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Dimensions

Initialize a new Dimension object.

The options argument is a hash which represents the base dimensions that define the physical quantity. Each key-value pair should consist of a key included in the BASE_QUANTITIES array, and a value which represents the index/power of that base quantity.

In addition, a name or description of the physical quantity can be specified (i.e. ‘acceleration’, ‘electric_current’). This is optional for creating a new Dimensions instance, but required if that object is to be loaded into the @@dimensions class array. e.g.:

Dimensions.new :physical_quantity => :density,
               :mass => 1,
               :length => -3


184
185
186
187
188
189
190
# File 'lib/quantify/dimensions.rb', line 184

def initialize(options={})
  if options.has_key?(:physical_quantity)
    @physical_quantity = options.delete(:physical_quantity).remove_underscores.downcase
  end
  enumerate_base_quantities(options)
  describe
end

Instance Attribute Details

#physical_quantityObject

Returns the value of attribute physical_quantity.



166
167
168
# File 'lib/quantify/dimensions.rb', line 166

def physical_quantity
  @physical_quantity
end

Class Method Details

.base_dimensionsObject

Returns an array of Dimensions objects representing just the base quantities, i.e. length, mass, time, temperature, etc.



52
53
54
55
56
# File 'lib/quantify/dimensions.rb', line 52

def self.base_dimensions
  @@dimensions.select do |dimensions|
    BASE_QUANTITIES.map {|quantity| quantity.remove_underscores}.include?(dimensions.describe)
  end
end

.configure(&block) ⇒ Object

Syntactic sugar for defining the known quantities. This method simply evaluates code within the context of the Dimensions class, enabling the required quantities to be loaded at runtime, e.g.

Dimensions.configure do

  load :physical_quantity => :length, :length => 1
  load :physical_quantity => :area, :length => 2
  load :physical_quantity => :power, :mass => 1, :length => 2, :time => -3

end


142
143
144
# File 'lib/quantify/dimensions.rb', line 142

def self.configure(&block)
    self.class_eval(&block) if block
end

.dimensionsObject

Provides access the class array which holds all defined quantities



45
46
47
# File 'lib/quantify/dimensions.rb', line 45

def self.dimensions
  @@dimensions
end

.for(name) ⇒ Object

Retrieve a known quantity - returns a Dimensions instance, which is a clone of the initialized instance of the specified quantity. This enables the object to be modified/manipulated without corrupting the representation of the quantity in the @@dimensions class array.

The required quantity name/descriptor can be specified as a symbol or a string, e.g.:

Dimensions.for :acceleration
Dimensions.for 'luminous_flux'

These can be shortened to, e.g. Dimensions.acceleration by virtue of the #method_missing class method (below)



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

def self.for(name)
  return name if name.is_a? Dimensions
  if (name.is_a?(String) || name.is_a?(Symbol))
    name = name.remove_underscores.downcase
    if quantity = @@dimensions.find { |quantity| quantity.physical_quantity == name }
      return quantity.clone
    else
      raise Exceptions::InvalidArgumentError, "Physical quantity not known: #{name}"
    end
  else
    raise Exceptions::InvalidArgumentError, "Argument must be a Symbol or String"
  end
end

.load(options) ⇒ Object

This method allows specific, named quantities to be initialized and loaded into the @@dimensions array. Quantities are specified by their consituent base dimensions, but must also include a name/description, i.e. ‘acceleration’, :force - indicated by the :physical_quantity key - in order to be included in the system of known dimensions, e.g.:

Dimensions.load :physical_quantity => :force,
                :length => 1,
                :mass => 1,
                :time => -2

Standard quantities such as force, energy, mass, etc. should not need to be defined as they are included in the set of quantities already defined (see config.rb) and automatically loaded. These can be removed, overridden or configured differently if desired.



74
75
76
77
78
79
80
# File 'lib/quantify/dimensions.rb', line 74

def self.load(options)
  if options[:physical_quantity]
    @@dimensions << Dimensions.new(options)
  else
    raise Exceptions::InvalidDimensionError, "Cannot load dimensions without physical quantity description"
  end
end

.method_missing(method, *args, &block) ⇒ Object

Provides a shorthand for retrieving known quantities, e.g.:

Dimensions.force

is equivalent to

Dimensions.for :force

Both variants return a clone of the initialized dimensional representation of the specified physical quantity (i.e. force).



157
158
159
160
161
162
# File 'lib/quantify/dimensions.rb', line 157

def self.method_missing(method, *args, &block)
  if dimensions = self.for(method)
    return dimensions
  end
  super
end

.physical_quantitiesObject

Returns an array containing the names/descriptions of all known (loaded) physical quantities, e.g.:

Dimensions.physical_quantities     #=>    [ 'acceleration',
                                            'area',
                                            'electric Current',
                                             ... ]


98
99
100
# File 'lib/quantify/dimensions.rb', line 98

def self.physical_quantities
  @@dimensions.map {|dimension| dimension.physical_quantity }
end

.unload(*unloaded_dimensions) ⇒ Object

Remove a dimension from the system of known dimensions



83
84
85
86
87
88
# File 'lib/quantify/dimensions.rb', line 83

def self.unload(*unloaded_dimensions)
  [unloaded_dimensions].flatten.each do |unloaded_dimension|
    unloaded_dimension = Dimensions.for(unloaded_dimension)
    @@dimensions.delete_if { |dimension| dimension.has_same_identity_as? unloaded_dimension }
  end
end

Instance Method Details

#==(other) ⇒ Object

Compares the base quantities of two Dimensions objects and returns true if they are the same. This indicates that the two objects represent the same physical quantity (irrespective of their names - @physical_quantity - being similar, different, or absent.



330
331
332
# File 'lib/quantify/dimensions.rb', line 330

def ==(other)
  self.to_hash == other.to_hash
end

#describeObject

Return a description of what physical quantity self represents. If no value is found in the @physical_quantity instance variable, the task is delegated to the #get_description method.



226
227
228
# File 'lib/quantify/dimensions.rb', line 226

def describe
  @physical_quantity or get_description
end

#divide(other) ⇒ Object Also known as: /

Similar to #divide! but returns a new Dimensions instance representing the physical quantity which results from the division.



399
400
401
# File 'lib/quantify/dimensions.rb', line 399

def divide(other)
  Dimensions.new(self.to_hash).divide! other
end

#divide!(other) ⇒ Object

Similar to #multiply! but performs a division of self by the specified Dimensions object.



390
391
392
393
394
# File 'lib/quantify/dimensions.rb', line 390

def divide!(other)
  enumerate_base_quantities(other.reciprocalize.to_hash)
  get_description
  return self
end

#get_descriptionObject

Searches the system of known physical quantities (@@dimensions class array) looking for any which match self in terms of the configuration of base dimensions, i.e. an object which dimensionally represents the same thing.

If found, the name/description of that quantity is assigned to the This method is useful in cases where Dimensions instances are manipulated using operators (e.g. multiply, divide, power, reciprocal), resulting in a change to the configuration of base dimensions (perhaps as a new instance). This method tries to find a description of the new quantity.

If none is found, self.physical_quantity is set to nil.



245
246
247
248
# File 'lib/quantify/dimensions.rb', line 245

def get_description
  similar = @@dimensions.find { |quantity| quantity == self }
  @physical_quantity = similar.nil? ? nil : similar.physical_quantity
end

#has_same_identity_as?(other) ⇒ Boolean

Returns:

  • (Boolean)


218
219
220
# File 'lib/quantify/dimensions.rb', line 218

def has_same_identity_as?(other)
  @physical_quantity == other.physical_quantity && !@physical_quantity.nil?
end

#is_base?Boolean

Returns true if self represents one of the base quantities (i.e. length, mass, time, etc.)

Returns:

  • (Boolean)


346
347
348
349
# File 'lib/quantify/dimensions.rb', line 346

def is_base?
  base_quantities.size == 1 &&
    self.instance_variable_get(base_quantities.first) == 1 ? true : false
end

#is_dimensionless?Boolean

Returns true if self is a dimensionless quantity

Returns:

  • (Boolean)


340
341
342
# File 'lib/quantify/dimensions.rb', line 340

def is_dimensionless?
  base_quantities.empty?
end

#is_known?Boolean

Returns true if the physical quantity that self represents is known

Returns:

  • (Boolean)


335
336
337
# File 'lib/quantify/dimensions.rb', line 335

def is_known?
  describe ? true : false
end

#is_molar_quantity?Boolean

Method for identifying quantities which are ‘molar’ quantities, i.e quantities which represent a quantity of something *per mole*

Returns:

  • (Boolean)


361
362
363
# File 'lib/quantify/dimensions.rb', line 361

def is_molar_quantity?
  denominator_quantities == [:@amount_of_substance]
end

#is_specific_quantity?Boolean

Method for identifying quantities which are ‘specific’ quantities, i.e quantities which represent a quantity of something *per unit mass*

Returns:

  • (Boolean)


354
355
356
# File 'lib/quantify/dimensions.rb', line 354

def is_specific_quantity?
  denominator_quantities == [:@mass]
end

#loadObject

Load an already instantiated Dimensions object into the @@dimensions class array, from which it will be accessible as a universal representation of that physical quantity.

Object must include a non-nil @physical_quantity attribute, i.e. a name or description of the physical quantity represented.



199
200
201
202
203
204
205
206
207
# File 'lib/quantify/dimensions.rb', line 199

def load
  if describe && !loaded?
    @@dimensions << self
  elsif describe
    raise Exceptions::InvalidDimensionError, "A dimension instance with the same physical quantity already exists"
  else
    raise Exceptions::InvalidDimensionError, "Cannot load dimensions without physical quantity description"
  end
end

#loaded?Boolean

Returns:

  • (Boolean)


209
210
211
# File 'lib/quantify/dimensions.rb', line 209

def loaded?
  Dimensions.dimensions.any? { |quantity| self.has_same_identity_as? quantity }
end

#multiply(other) ⇒ Object Also known as: times, *

Similar to #multiply! but returns a new Dimensions instance representing the physical quantity which results from the multiplication.



381
382
383
# File 'lib/quantify/dimensions.rb', line 381

def multiply(other)
  Dimensions.new(self.to_hash).multiply! other
end

#multiply!(other) ⇒ Object

Multiplies self by another Dimensions object, returning self with an updated configuration of dimensions. Since this is likely to have resulted in the representation of a different physical quantity than was originally represented, the #get_description method is invoked to attempt to find a suitable description.



372
373
374
375
376
# File 'lib/quantify/dimensions.rb', line 372

def multiply!(other)
  enumerate_base_quantities(other.to_hash)
  get_description
  return self
end

#pow(power) ⇒ Object Also known as: **

Similar to #pow! but returns a new Dimensions instance representing the physical quantity which results from the raised power.



423
424
425
# File 'lib/quantify/dimensions.rb', line 423

def pow(power)
  Dimensions.new(self.to_hash).pow!(power)
end

#pow!(power) ⇒ Object

Raises self to the power provided. As with multiply and divide, the #get_description method is invoked to attempt to find a suitable description for the new quantity represented.



408
409
410
411
412
413
414
415
416
417
418
# File 'lib/quantify/dimensions.rb', line 408

def pow!(power)
  make_dimensionless if power == 0
  if power < 0
    self.reciprocalize!
    power *= -1
  end
  original_dimensions = self.clone
  (power - 1).times { self.multiply!(original_dimensions) }
  get_description
  return self
end

#reciprocalizeObject

Similar to #reciprocalize! but returns a new Dimensions instance representing the physical quantity which results from the inversion.



444
445
446
# File 'lib/quantify/dimensions.rb', line 444

def reciprocalize
  Dimensions.new(self.to_hash).reciprocalize!
end

#reciprocalize!Object

Inverts self, returning a representation of 1/self. This is equivalent to raising to the power -1. The #get_description method is invoked to attempt to find a suitable description for the new quantity represented.



432
433
434
435
436
437
438
439
# File 'lib/quantify/dimensions.rb', line 432

def reciprocalize!
  base_quantities.each do |variable|
    new_value = self.instance_variable_get(variable) * -1
    self.instance_variable_set(variable, new_value)
  end
  get_description
  return self
end

#si_base_units(by = nil) ⇒ Object

Returns an array representing the base SI units for the physical quantity described by self

If no argument is given, the array holds instances of Unit::Base (or subclasses) which represent each base unit. Alternatively only the names or symbols of each unit can be returned by providing the appropriate unit attribute as a symbolized argument, e.g.

Dimensions.energy.si_base_units       #=> [ #<Quantify::Unit: .. >,
                                            #<Quantify::Unit: .. >,
                                            ... ]

Dimensions.energy.si_base_units :name

                                      #=> [ "metre squared",
                                            "per second squared",
                                            "kilogram"]    #

Dimensions.force.units :symbol        #=> [ "m", "s^-2", "kg"]


317
318
319
320
321
322
323
# File 'lib/quantify/dimensions.rb', line 317

def si_base_units(by=nil)
  self.to_hash.map do |dimension,index|
    Unit.base_quantity_si_units.select do |unit|
      unit.measures == dimension.remove_underscores
    end.first.clone ** index
  end.map(&by).to_a
end

#si_unitObject

Returns the SI unit for the physical quantity described by self.

Plane/solid angle are special cases which are dimensionless units, and so are handled explicitly. Otherwise, the si base units for each of the base dimensions of self are indentified and the corresponding compound unit is derived. If this new unit is the same as a known (SI derived) unit, the known unit is returned.

Dimensions.energy.units                  #=> #<Quantify::Dimensions: .. >

Dimensions.energy.si_unit.name           #=> 'joule'

Dimensions.kinematic_viscosity.si_unit.name

                                         #=> 'metre squared per second'


287
288
289
290
291
292
293
294
295
# File 'lib/quantify/dimensions.rb', line 287

def si_unit
  return Unit.steridian if describe == 'solid angle'
  return Unit.radian if describe == 'plane angle' 
  return si_base_units.inject(Unit.unity) do |compound,unit|
    compound * unit
  end.or_equivalent
rescue
  return nil
end

#units(by = nil) ⇒ Object

Returns an array containing the known units which represent the physical quantity described by self

If no argument is given, the array holds instances of Unit::Base (or subclasses) which represent each unit. Alternatively only the names or symbols of each unit can be returned by providing the appropriate unit attribute as a symbolized argument, e.g.

Dimensions.energy.units             #=> [ #<Quantify::Dimensions: .. >,
                                          #<Quantify::Dimensions: .. >,
                                          ... ]

Dimensions.mass.units :name         #=> [ 'kilogram', 'ounce',
                                          'pound', ... ]

Dimensions.length.units :symbol     #=> [ 'm', 'ft', 'yd', ... ]


267
268
269
# File 'lib/quantify/dimensions.rb', line 267

def units(by=nil)
  Unit.units.select { |unit| unit.dimensions == self }.map(&by).to_a
end

#unloadObject

Remove from system of known units.



214
215
216
# File 'lib/quantify/dimensions.rb', line 214

def unload
  Dimensions.unload(self.physical_quantity)
end