Class: Quantify::Unit::Compound

Inherits:
Base
  • Object
show all
Defined in:
lib/quantify/unit/compound_unit.rb

Instance Attribute Summary collapse

Attributes inherited from Base

#acts_as_alternative_unit, #dimensions, #factor, #label, #name, #symbol

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#alternatives, #canonical_label=, #coerce, configure, #configure, #configure_as_canonical, construct, construct_and_load, #divide, #has_same_identity_as?, #has_scaling?, #initialize_copy, #is_alternative_for?, #is_base_unit?, #is_benchmark_unit?, #is_compound_unit?, #is_derived_unit?, #is_dimensionless?, #is_equivalent_to?, #is_prefixed_unit?, #load, load, #loaded?, #make_canonical, #measures, #method_missing, #multiply, #pow, prefix_and_load, #reciprocalize, #refresh_identifiers!, #scaling, #si_unit, #to_hash, #unload, #valid?, #valid_descriptors?, #valid_dimensions?, #valid_prefixes, #with_prefix, #with_prefixes

Methods included from ExtendedMethods

#method_missing

Constructor Details

#initialize(*units) ⇒ Compound

Initialize a compound unit by providing an array containing a represenation of each base unit.

Array may contain elements specified as follows:

1. a instance of CompoundBaseUnit

2. an instance of Unit::Base (in which case its index is assumed as 1

3. a sub-array of size 2 containing an instance of Unit::Base and an
   explicit index


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/quantify/unit/compound_unit.rb', line 89

def initialize(*units)
  @base_units = []
  units.each do |unit|
    if unit.is_a? CompoundBaseUnit
      @base_units << unit
    elsif unit.is_a? Unit::Base
      @base_units << CompoundBaseUnit.new(unit)
    elsif unit.is_a?(Array) && unit.first.is_a?(Unit::Base) &&
        !unit.first.is_a?(Compound) && unit.size == 2
      @base_units << CompoundBaseUnit.new(unit.first,unit.last)
    else
      raise Exceptions::InvalidArgumentError, "#{unit} does not represent a valid base unit"
    end
  end
  @acts_as_alternative_unit = true
  @acts_as_equivalent_unit = false
  consolidate_numerator_and_denominator_units!
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Quantify::Unit::Base

Instance Attribute Details

#acts_as_equivalent_unitObject (readonly)

Returns the value of attribute acts_as_equivalent_unit.



75
76
77
# File 'lib/quantify/unit/compound_unit.rb', line 75

def acts_as_equivalent_unit
  @acts_as_equivalent_unit
end

#base_unitsObject (readonly)

Returns the value of attribute base_units.



75
76
77
# File 'lib/quantify/unit/compound_unit.rb', line 75

def base_units
  @base_units
end

Class Method Details

.consolidate_base_units(base_units) ⇒ Object

Consilidates base quantities by finding multiple instances of the same unit type and reducing them into a single unit represenation, by altering the repsective index. It has the effect of raising units to powers and cancelling those which appear in the numerator AND denominator

This is a class method which takes an arbitrary array of base units as an argument. This means that consolidation can be performed on either all base units or just a subset - the numerator or denominator units.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/quantify/unit/compound_unit.rb', line 34

def self.consolidate_base_units(base_units)
  raise Exceptions::InvalidArgumentError, "Must provide an array of base units" unless base_units.is_a? Array

  new_base_units = []

  while base_units.size > 0 do
    new_base = base_units.shift
    next if new_base.unit.is_dimensionless?

    new_base.index = base_units.select do |other_base|
      new_base.unit.is_equivalent_to? other_base.unit
    end.inject(new_base.index) do |index,other_base|
      base_units.delete other_base
      index += other_base.index
    end

    new_base_units << new_base unless new_base.is_dimensionless?
  end
  return new_base_units
end

.rationalize_base_units(base_units = [], *required_units) ⇒ Object

Make compound unit use consistent units for representing each physical quantity. For example, lb/kg => kg/kg.

This is a class method which takes an arbitrary array of base units as an argument. This means that consolidation can be performed on either all base units or just a subset - e.g. the numerator or denominator units.

The units to use for particular physical dimension can be specified following the inital argument. If no unit is specified for a physical quantity which is represented in the array of base units, then the first unit found for that physical quantity is used as the canonical one.



67
68
69
70
71
72
73
# File 'lib/quantify/unit/compound_unit.rb', line 67

def self.rationalize_base_units(base_units=[],*required_units)
  base_units.each do |base|
    new_unit = required_units.map { |unit| Unit.for(unit) }.find { |unit| unit.measures == base.measures } ||
      base_units.find { |unit| unit.measures == base.measures }.unit
    base.unit = new_unit
  end
end

Instance Method Details

#cancel_base_units!(*units) ⇒ Object

Cancel base units across numerator and denominator. If similar units occur in both the numerator and denominator, they can be cancelled, i.e. their powers reduced correspondingly until one is removed.

This method is useful when wanting to remove specific units that can be cancelled from the compound unit configuration while retaining the remaining units in the current format.

If no other potentially cancelable units need to be retained, the method #consolidate_base_units! can be called with the :full argument instead

This method takes an arbitrary number of arguments which represent the units which are required to be cancelled (string, symbol or object)



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/quantify/unit/compound_unit.rb', line 163

def cancel_base_units!(*units)
  units.each do |unit|
    raise Exceptions::InvalidArgumentError, "Cannot cancel by a compound unit" if unit.is_a? Unit::Compound
    unit = Unit.for unit unless unit.is_a? Unit::Base

    numerator_unit = numerator_units.find { |base| unit.is_equivalent_to? base.unit }
    denominator_unit = denominator_units.find { |base| unit.is_equivalent_to? base.unit }

    if numerator_unit && denominator_unit
      cancel_value = [numerator_unit.index,denominator_unit.index].min.abs
      numerator_unit.index -= cancel_value
      denominator_unit.index += cancel_value
    end
  end
  consolidate_numerator_and_denominator_units!
end

#consolidate_base_units!Object

Consolidate base units. A ‘full’ consolidation is performed, i.e. consolidation across numerator and denominator. This is equivalent to the automatic partial consolidation AND a cancelling of units (i.e. #cancel_base_units!)



143
144
145
146
147
# File 'lib/quantify/unit/compound_unit.rb', line 143

def consolidate_base_units!
  @base_units = Compound.consolidate_base_units(@base_units)
  initialize_attributes
  return self
end

#denominator_unitsObject

Returns an array containing only the base units which have negative indices



115
116
117
# File 'lib/quantify/unit/compound_unit.rb', line 115

def denominator_units
  @base_units.select { |base| base.is_denominator? }
end

#equivalent_known_unitObject

Return a known unit which is equivalent to self in terms of its physical quantity (dimensions), factor and scaling attributes (i.e. representing the precise same physical unit but perhaps with different identifiers), e.g.

((Unit.kg*(Unit.m**"))/(Unit.s**2)).equivalent_known_unit.name

                             #=> "joule"


212
213
214
215
216
# File 'lib/quantify/unit/compound_unit.rb', line 212

def equivalent_known_unit
  Unit.units.find do |unit|
    self.is_equivalent_to?(unit) && !unit.is_compound_unit?
  end
end

#is_base_quantity_si_unit?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/quantify/unit/compound_unit.rb', line 134

def is_base_quantity_si_unit?
  @base_units.all? { |base| base.is_base_quantity_si_unit? }
end

#is_non_si_unit?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/quantify/unit/compound_unit.rb', line 130

def is_non_si_unit?
  @base_units.any? { |base| base.is_non_si_unit? }
end

#is_si_unit?Boolean

Determine is a unit object represents an SI named unit.

Returns:

  • (Boolean)


126
127
128
# File 'lib/quantify/unit/compound_unit.rb', line 126

def is_si_unit?
  @base_units.all? { |base| base.is_si_unit? }
end

#numerator_unitsObject

Returns an array containing only the base units which have positive indices



110
111
112
# File 'lib/quantify/unit/compound_unit.rb', line 110

def numerator_units
  @base_units.select { |base| base.is_numerator? }
end

#or_equivalentObject

Returns an equivalent known unit (via #equivalent_known_unit) if it exists. Otherwise, returns false.



221
222
223
224
225
226
227
228
# File 'lib/quantify/unit/compound_unit.rb', line 221

def or_equivalent
  equivalent_unit = equivalent_known_unit
  if equivalent_unit && equivalent_unit.acts_as_equivalent_unit
    return equivalent_unit
  else
    return self
  end
end

#pluralized_nameObject

Convenient accessor method for pluralized names



120
121
122
# File 'lib/quantify/unit/compound_unit.rb', line 120

def pluralized_name
  derive_name :plural
end

#rationalize_base_units!(scope = :partial, *units) ⇒ Object

Make the base units of self use consistent units for each physical quantity represented. For example, lb/kg => kg/kg.

By default, units are rationalized within the the numerator and denominator respectively. That is, different units representing the same physical quantity may appear across the numerator and denominator, but not within each. To fully rationalize the base units of self, pass in the symbol :full as a first argument. Otherwise :partial is passed as the default.

The units to use for particular physical dimension can be specified following the inital argument. If no unit is specified for a physical quantity which is represented in the array of base units, then the first unit found for that physical quantity is used as the canonical one.



194
195
196
197
198
199
200
201
202
# File 'lib/quantify/unit/compound_unit.rb', line 194

def rationalize_base_units!(scope=:partial,*units)
  if scope == :full
    Compound.rationalize_base_units(@base_units,*units)
  else
    Compound.rationalize_base_units(numerator_units,*units)
    Compound.rationalize_base_units(denominator_units,*units)
  end
  consolidate_numerator_and_denominator_units!
end