Module: SY

Defined in:
lib/sy.rb,
lib/sy.rb,
lib/sy/version.rb,
lib/sy/imperial.rb,
lib/sy/noinclude.rb,
lib/sy/fixed_assets_of_the_module.rb

Overview

Here, fixed assets of the main module are set up.

Defined Under Namespace

Modules: AbsoluteMagnitude, CelsiusMagnitude, ExpressibleInUnits, Magnitude, SignedMagnitude, Unit Classes: CelsiusTemperature.relative.send(:Magnitude), CelsiusTemperature.send(:Magnitude), Composition, Dimension, Measure, Numeric, Quantity

Constant Summary collapse

AUTOINCLUDE =
false
Amount =

Let SY::Amount be a standard dimensionless quantity:

Quantity.standard of: Dimension.zero
UNIT =

Let SY::UNIT be a standard unit of SY::Amount. Note that the upcase name of the constant “UNIT” implies, via YSupport’s NameMagic mixin, that the name of the object becomes :unit and that it is possible to use syntax such as 42.unit to create magnitudes of SY::Amount.

Unit.standard of: Amount
Nᴀ =

AVOGADRO_CONSTANT (Nᴀ) is a certain well-known amount of things:

AVOGADRO_CONSTANT = 6.02214e23
MoleAmount =

Let SY::MoleAmount be another dimensionless quantity:

Quantity.dimensionless coerces: Amount
MOLE =

And let SY::MOLE be its standard unit, related to SY::Amount via Nᴀ:

Unit.standard of: MoleAmount, short: "mol", amount: Nᴀ * UNIT
Length =

Let SY::Length be a standard quantity of basic dimension L:

Quantity.standard of: :L
METRE =

Let SY::METRE be its standard unit.

Unit.standard of: Length, short: "m"
Mass =

Let SY::Mass be a standard quantity of basic dimension M:

Quantity.standard of: :M
KILOGRAM =

Let SY::KILOGRAM be its standard unit:

Unit.standard of: Mass, short: "kg"
GRAM =

Let SY::GRAM be another unit of SY::Mass, equal to 0.001.kg:

Unit.of Mass, amount: 0.001 * KILOGRAM, short: "g"
TON =

Let SY::TON be another…

Unit.of Mass, amount: 1000 * KILOGRAM, short: "t"
DALTON =

And SY::DALTON another…

Unit.of Mass,
short: "Da",
amount: 1.66053892173e-27 * KILOGRAM
Time =

Let SY::Time be a standard quantity of basic dimension T:

Quantity.standard of: :T
SECOND =

Let SY::SECOND be its standard unit:

Unit.standard of: Time, short: "s"
MINUTE =

Let SY::MINUTE be another unit:

Unit.of Time, short: "min", amount: 60 * SECOND
HOUR =

And SY::HOUR another:

Unit.of Time, short: "h", amount: 60 * MINUTE
DAY =

SY::DAY is defined in accordance with SI as 86_400.second

Unit.of Time, amount: 24 * HOUR
WEEK =

SY::WEEK means exactly seven days

Unit.of Time, amount: 7 * DAY
SYNODIC_MONTH =

Average lunar month.

29.530589 * DAY
YEAR =

Julian year.

JULIAN_YEAR = 365.25 * DAY
ElectricCharge =

Let SY::ElectricCharge be a standard quantity of basic dimension Q:

Quantity.standard of: :Q
COULOMB =

And SY::COULOMB be its standard unit:

Unit.standard of: ElectricCharge, short: "C"
Temperature =

Let SY::Temperature be a standard quantity of basic dimension Θ:

Quantity.standard of: 
KELVIN =

And SY::KELVIN be its standard unit:

Unit.standard of: Temperature, short: "K"
TP_H₂O =

Now let us define a useful constant:

TRIPLE_POINT_OF_WATER = 273.15 * KELVIN
CelsiusTemperature =

Celsius temperature is a little bit peculiar in that it has offset of 273.15.K with respect to Kelvin temperature, and I am not sure whether at this moment SY is handling this right. But nevertheless:

Quantity.of , coerces_to: Temperature
CELSIUS_MEASURE =

CELSIUS_MEASURE is an object of SY::Measure class, that captures the conversion between centigrades and Kelvins.

SY::Measure.simple_offset( TRIPLE_POINT_OF_WATER.to_f )
CELSIUS =

Degree celsius is SY::CELSIUS

Unit.standard( of: CelsiusTemperature,
short: '°C', measure: CELSIUS_MEASURE )
HUMAN_BODY_TEMPERATURE =
TP_H₂O + 37 * KELVIN
STANDARD_LABORATORY_TEMPERATURE =
TP_H₂O + 25 * KELVIN
Area =

Quantity SY::Area is obtained by raising quantity SY::Length to 2:

Length ** 2
Volume =

Quantity SY::Volume is obtained by raising quantity SY::Length to 3:

Length ** 3
LitreVolume =

SY::LitreVolume is another quantity of the same dimension as SY::Volume:

Quantity.of Volume.dimension, coerces_to: Volume
LITRE =

SY::LITRE is the standard unit of SY::LitreVolume:

Unit.standard of: LitreVolume, short: "l", amount: 0.001 * METRE ** 3
Molarity =

SY::Molarity is obtained by dividing SY::MoleAmount by SY::LitreVolume:

( MoleAmount / LitreVolume ).protect!
MOLAR =

Standard unit of SY::Molarity is SY::MOLAR:

Unit.standard of: Molarity, short: "M"
Frequency =

SY::Frequency, a quantity that many will expect:

1 / Time
HERTZ =

SY::HERTZ is its unit:

Unit.of Frequency, short: "Hz"
Speed =

Define SY::Speed as SY::Length / SY::Time and make it a standard quantity of its dimension.

( Length / Time ).standard!
SPEED_OF_LIGHT =

Commonly used constant.

299_792_458 * METRE / SECOND
LIGHTYEAR =

Supplementary unit of length.

Unit.of Length, short: "ly", amount: SPEED_OF_LIGHT * JULIAN_YEAR
Acceleration =

Similar for SY::Acceleration:

( Speed / Time ).standard!
Force =

For SY::Force…

( Acceleration * Mass ).standard!
NEWTON =

This time, make SY::NEWTON its standard unit:

Unit.standard of: Force, short: "N"
Energy =

For SY::Energy…

( Force * Length ).standard!
JOULE =

Make SY::JOULE its standard unit:

Unit.standard of: Energy, short: "J"
CALORIE =

SY::CALORIE means thermochemical calorie:

Unit.of Energy, short: "cal", amount: 4.184 * JOULE
Power =

SY::Power…

( Energy / Time ).standard!
WATT =

make SY::WATT its standard unit:

Unit.standard of: Power, short: "W"
WATTHOUR =

Watthour (Wh) is a common unit of energy.

Unit.of Energy, short: "Wh", amount: WATT * HOUR
Pressure =

SY::Pressure…

( Force / Area ).standard!
PASCAL =

make SY::PASCAL its standard unit:

Unit.standard of: Pressure, short: "Pa"
ElectricCurrent =

SY::ElectricCurrent…

( ElectricCharge / Time ).standard!
AMPERE =

make SY::AMPERE its standard unit:

Unit.standard of: ElectricCurrent, short: "A"
ElectricPotential =

SY::ElectricPotential…

( Energy / ElectricCharge ).standard!
VOLT =

make SY::VOLT its standard unit:

Unit.standard of: ElectricPotential, short: "V"
Molality =

SY::Molality…

MoleAmount / Mass
MOLAL =

make SY::MOLAL its unit (but don’t make it a standard unit…):

Unit.of Molality
Molecularity =

SY::Molecularity…

Amount / LitreVolume
Kʙ =

Having defined Joules and Kelvins, we can spell out the Boltzmann constant:

BOLTZMANN_CONSTANT = 1.380648813e-23 * JOULE / KELVIN
ELEMENTARY_CHARGE =
1.60217656535e-19 * COULOMB
ELECTRONVOLT =
Unit.of Energy, short: "eV", amount: ELEMENTARY_CHARGE * VOLT
VERSION =
"2.1.5"
INCH =

Length

Unit.of Length, amount: 25.4 * 0.001 * METRE
FOOT =

short: ‘in’ would be ambiguous

Unit.of Length, short: 'ft', amount: 12 * INCH
YARD =
Unit.of Length, short: 'yd', amount: 3 * FOOT
FURLONG =

forget CHAIN

Unit.of Length, short: 'fur', amount: 220 * YARD
MILE =
Unit.of Length, short: 'mi', amount: 5_280 * FOOT
FATHOM =
Unit.of Length, short: 'ftm', amount: 1.853184 * METRE
NAUTICAL_MILE =
Unit.of Length, amount: 1000 * FATHOM
ACRE =

Area

Unit.of Area, amount: ( 1.0 / 640 ) * MILE ** 2
PINT =

Volume

Unit.of Volume, amount: 568.26125 * ( 0.01 * METRE ) ** 3
QUART =

FIXME: PINT = Unit.of Volume, amount: 568.26125.ml didn’t work, it gave 1000 times more value something is wrong with the conversion mechanics

Unit.of Volume, amount: 2 * PINT
GALLON =
Unit.of Volume, short: 'gal', amount: 8 * PINT
POUND =

Mass

Unit.of Mass, short: 'lb', amount: 453.59237 * GRAM
OUNCE =
Unit.of Mass, short: 'oz', amount: ( 1.0 / 16 ) * POUND
STONE =
Unit.of Mass, amount: 14 * POUND
FIRKIN =
Unit.of Mass, short: 'fir', amount: 90 * POUND
IMPERIAL_TON =
Unit.of Mass, amount: 2240 * POUND
FORTNIGHT =

Time

Unit.of Time, short: 'ftn', amount: 1_209_600 * SECOND
MPH =

Speed

Unit.of Speed, amount: MILE / HOUR
QuantityError =

incompatible quantities

Class.new TypeError
DimensionError =

incompatible dimensions

Class.new TypeError
MagnitudeError =

creating impossible magnitude

Class.new TypeError
BASE_DIMENSIONS =

Basic physical dimensions.

{                # Basic physical dimensions.
  L: :LENGTH,
  M: :MASS,
  T: :TIME,
  Q: :ELECTRIC_CHARGE,        # instead of electric current
  Θ: :TEMPERATURE,
}
PREFIX_TABLE =

Table of standard prefixes and their corresponding unit multiples.

[ { full: "exa", short: "E", factor: 1e18 },
{ full: "peta", short: "P", factor: 1e15 },
{ full: "tera", short: "T", factor: 1e12 },
{ full: "giga", short: "G", factor: 1e9 },
{ full: "mega", short: "M", factor: 1e6 },
{ full: "kilo", short: "k", factor: 1e3 },
{ full: "hecto", short: "h", factor: 1e2 },
{ full: "deka", short: "dk", factor: 1e1 },
{ full: "", short: "", factor: 1 },
{ full: "deci", short: "d", factor: 1e-1 },
{ full: "centi", short: "c", factor: 1e-2 },
{ full: "mili", short: "m", factor: 1e-3 },
{ full: "micro", short: "µ", factor: 1e-6 },
{ full: "nano", short: "n", factor: 1e-9 },
{ full: "pico", short: "p", factor: 1e-12 },
{ full: "femto", short: "f", factor: 1e-15 },
{ full: "atto", short: "a", factor: 1e-18 } ]
SUPERSCRIPT =

Unicode superscript exponents.

SUPERSCRIPT_DOWN =

Reverse conversion of Unicode superscript exponents (from exponent strings to fixnums).

Hash.new { |, key|
    if key.is_a? String then
      key.size == 1 ? nil : key.each_char.map{|c| [c] }.join
    else [key.to_s] end
  }.merge!( SUPERSCRIPT.invert ).merge!( '¯' => '-', # other superscript chars
'´' => '/' )
SPS =

SPS stands for “superscripted product string”, It is a string of specific symbols with or without Unicode exponents, separated by periods, such as “syma.symb².symc⁻³.symd.syme⁴” etc. This closure takes 2 arguments (array of symbols, and array of exponents) and produces an SPS out of them.

lambda { |ßs, exps|
  raise ArgumentError unless ßs.size == exps.size
  exps = exps.map{|e| Integer e }
  zipped = ßs.zip( exps )
  clean = zipped.reject {|e| e[1] == 0 }
  # omit exponents equal to 1:
  clean.map{|ß, exp| "#{ß}#{exp == 1 ? "" : SUPERSCRIPT[exp]}" }.join "."
}
SPS_PARSER =

A closure that parses superscripted product strings (SPSs). It takes 3 arguments: a string to be parsed, an array of acceptable symbols, and an array of acceptable prefixes. It returns 3 equal-sized arrays: prefixes, symbols and exponents.

lambda { |input_ς, ßs, prefixes = []|
  input_ς = input_ς.to_s.strip
  ßs = ßs.map &:to_s
  prefixes = ( prefixes.map( &:to_s ) << '' ).uniq
  # input string splitting
  input_ς_sections = input_ς.split '.'
  if input_ς_sections.empty?
    raise NameError, "Bad input string: '#{input_ς}'!" unless input_ς.empty?
    return [], [], []
  end
  # analysis of input string sections
  input_ς_sections.each_with_object [[], [], []] do |_section_, memo|
    section = _section_.dup
    superscript_chars = SUPERSCRIPT.values
    # chop off the superscript tail, if any
    section.chop! while superscript_chars.any? { |ch| section.end_with? ch }
    # the set of candidate unit symbols
    candidate_ßs = ßs.select { |ß| section.end_with? ß }
    # seek candidate prefixes corresponding to candidate_ßs
    candidate_prefixes = candidate_ßs.map { |ß| section[ 0..((-1) - ß.size) ] }
    # see which possible prefixes can be confirmed
    confirmed_prefixes = candidate_prefixes.select { |x| prefixes.include? x }
    # complain if no symbol matches sec
    raise NameError, "Unknown unit: '#{section}'!" if confirmed_prefixes.empty?
    # pay attention to ambiguity in prefix/symbol pair
    if confirmed_prefixes.size > 1 then
      if confirmed_prefixes.any? { |x| x == '' } then # prefer empty prefixes
        chosen_prefix = ''
      else
        raise NameError, "Ambiguity in interpretation of '#{section}'!"
      end
    else
      chosen_prefix = confirmed_prefixes[0]
    end
    # Based on it, interpret the section parts:
    unit_ς = section[ (chosen_prefix.size)..(-1) ]
    suffix = _section_[ ((-1) - chosen_prefix.size - unit_ς.size)..(-1) ]
    # Make the exponent string suffix into the exponent number:
    exponent_ς = SUPERSCRIPT_DOWN[ suffix ]
    # Complain if bad:
    raise NameError, "Malformed exponent in #{_section_}!" if exponent_ς.nil?
    exponent_ς = "1" if exponent_ς == '' # empty exponent string means 1
    exp = Integer exponent_ς
    raise NameError, "Zero exponents not allowed: #{exponent_ς}" if exp == 0
    # and store the interpretation
    memo[0] << chosen_prefix; memo[1] << unit_ς; memo[2] << exp
    memo
  end
}

Class Method Summary collapse

Class Method Details

.Amount(number) ⇒ Object

Convenitence constructor of amounts (SY::Amount if the standard dimensionless quantity of SY).



270
271
272
# File 'lib/sy/fixed_assets_of_the_module.rb', line 270

def Amount number
  SY::Amount.relative.magnitude( number )
end

.Dimension(id = proc{ return ::SY::Dimension }.call) ⇒ Object

Convenience dimension accessor.



241
242
243
244
245
# File 'lib/sy/fixed_assets_of_the_module.rb', line 241

def Dimension id=proc{ return ::SY::Dimension }.call
  case id.to_s
  when '', 'nil', 'null', 'zero', '0', '', '', 'ø' then SY::Dimension.zero
  else SY::Dimension.new id end
end

.Magnitude(args = proc{ return ::SY::Magnitude }.call) ⇒ Object

Explicit magnitude constructor.



261
262
263
264
265
# File 'lib/sy/fixed_assets_of_the_module.rb', line 261

def Magnitude args=proc{ return ::SY::Magnitude }.call
  args.must_have :quantity, syn!: :of
  qnt = args.delete :quantity
  SY::Magnitude.of qnt, args
end

.Quantity(id = proc{ return ::SY::Quantity }.call) ⇒ Object

Convenience quantity instance accessor.



249
250
251
# File 'lib/sy/fixed_assets_of_the_module.rb', line 249

def Quantity id=proc{ return ::SY::Quantity }.call
  SY::Quantity.instance id
end

.Unit(id = proc{ return ::SY::Unit }.call) ⇒ Object

Convenience unit instance accessor.



255
256
257
# File 'lib/sy/fixed_assets_of_the_module.rb', line 255

def Unit id=proc{ return ::SY::Unit }.call
  SY::Unit.instance id
end