Last published: July 5, 2013 by 'randy'
Defines 5 Classes
Extends 6 Classes
Quantities is an implementation of dimensional analysis in a generalized and extensible form. Unlike unit libraries like Boost.Units this library implements runtime unit computation and quantity arithmetic operations. The distinction is that multiplying two quantities together can produce a previous unknown unit combination, while Boost.Units requires the developer to declare the types as is the want of C++ and other manifestly typed languages.
This package provides support for units and quantities for arbitrary systems of measurement. If a unit can be defined in terms of an existing (and better yet, SI Unit) then automatic transformation between units with the same 'base unit' happens when you perform arithmetic operations. A large body of commonly used units are provided with the library, but more units can be added to the system at runtime. Care is taken to reduce runtime overhead when performing arithmetic operations.
This package borrows *heavily* from the package Measurements in the public store repository by Travis Griggs and Ken Greene who came up with the brilliant idea of using primes reduction to describe units in fractions.
The core of the package are the classes Unit, ArithmeticQuantity and Incompatible. Temperature and Angle are extensions to support richer domains and the class Duration which comes in the base image is reused to represent the Time dimension. Subclasses of ArithmeticQuantity can be defined to represent specific sets of units (for example, Angle represents Radians, Degrees and Gradians).
To quickly get started with units, look at the host of 'conversion' methods on ArithmeticValue, which will transform a regular scalar value in to a Quantity, eg: 5 seconds or 10 kilograms. Try combining units to create new combinations, such as a speed: 5 meters / 10 seconds. Units derived from SI base units are also supported, such as the Ω (ohm): 50 ohms. Force is represented in newtons, eg: 200 newtons. Which units get instance creation methods in the 'conversion' category of ArithmeticValue are defined mostly be convenience and how commonly they are used (as defined by Wikipedia). Feel free to extend ArithmeticValue with other units you use commonly.
Non SI Units are also included. Specifically, the commonly used measurements of distance used by the USA; 5 feet, 2 inches, 10 miles. Units can be converted between like-bases using the #as: method, which can also be used to transform a scalar in to a quantity, eg: (5 as: Unit.Second) and a conversion example: (3280.84 feet as: Unit.Kilometre).
The advantages of being able to perform arithmetic operations on quantities with different units that share the same base should be obvious to anyone who has tried to send a satellite to Mars. But in case you haven't tried that recently: 5 centimeters + 3 inches. Quantities can also be expanded dimensionally, for example: 5 meters ** 2. However, there is no support for reducing polynomials.
Units can be printed out to a stream with printOn: and read back from a stream with ArithmeticQuantity class>>readFrom:. Units names are also internationalized, so that if you live in a region where the unit names are unique you can redefine them using message catalogs.
Kinds of Units
A base unit is something that cannot be converted to another kind of unit. It is a domain within itself. There are some special cases to base units, such as dimensionless units like angles. Examples of things that are not base units are things like hours, which can be represented as seconds; kilometres which can be represented as metres; or feet which can also be represented as metres. Only one unit can represent a dimension as the base unit and in this package those base units are the SI base units.
A derived unit is a combination of base units which has been given a specific name, such as Hz (hertz) which is 1/s. A derived unit is not a unit that can be computed from another kind of unit, eg: a unit within the same dimension. An example of this would be feet which is in the same dimension as metre -- distance.
New units can be added to the system; to add a base unit that represents a new dimension or dimensionless domain, use Unit class>>addUnitNamed: and save the result in a variable somewhere. Quantities saves all the commonly used units in to shared variables on Unit itself, eg: Unit.Mole.
Derived units are constructed by multiplying or dividing units together and Quantities typically saves those in to shared variables too. You generally define a derived unit for one of two reasons: To give it a new concrete class or to give it a unique name. An example of this is how Ohm is constructed: (Ohm := Volt / Ampere) rename: #Ohm << #units >> 'Ω'. Here we're not sending addUnit: but simply constructing the unit and renaming it. Finally, scaled units are defined with Unit class>>addUnitNamed:base:scale:. You'd use this where a commonly used multiplying for an existing unit, say, MegaOhms was required, would be defined as MegaOhm := Unit addUnitNamed: #MegaOhm << #units >> 'MegaOhm' base: Ohm scale: Mega.
Only a couple of base units have concrete classes: Duration, Angle, Temperature. They exist because there are operations or conversions that are common for these units. Example, Duration is used for doing waits, Angle is used with trigonometric functions, Temperature is commonly converted between SI Kelvin, Celsius and Fahrenheit.
New concrete classes are made by subclassing ArithmeticQuantity and defining a pragma on the class side called which returns an array of Unit instances. I recommend you make sure the units are initialized before the pragma is executed.