# EiffelUnits

EiffelUnits is a library of units of measurement for theEiffel language. It enables you to attach units to the quantities you use in your programs. EiffelUnits includes a collection of common units along with conversion, formatting and consistency checking utilities. EiffelUnits is extensible and can be adapted to user-defined unit systems outside the SI (e.g. currencies, color values, ...). I only compiled it with ISE Eiffel 5.1 - if you've tried it with other compilers, please tell me.

Contents: Scope  ¤  Usage  ¤  Problems  ¤  Glossary  ¤  References

## Scope

### Why units?

Units of measurement are an indispensable tool in many branches of science. Our ancestor's invention of pure numbers was certainly a big step in human history. The abstraction of sets out of incommensurable goods (17 geese and 4 bottles of wine make 21 ... guess what? ... objects , of course) is often useful . But we all know that abstract classes are not enough. At times, it's recommendable to categorize values into meaningful dimensions, such that we can better track and understand what we are calculating.

Our knowledge of natural laws stems partly from a powerful technique called Dimensional Analysis. Basically, dimensioned quantities are nothing more than values attributed with a dimension. By noting these dimensions explicitly, we can check whether our equations and calculations are meaningful. All natural laws discovered so far are dimensionally consistent, which leads us to the presumption, that there really is such a thing as a `dimension' in nature. We can employ the dimensions of the quantities we use as another means of ensuring correctness in our programs because dimensionally inconsistent calculations are definitely wrong (and thus raise an exception when incommensurable units are used). Dimensioned quantities are a tool which enables you to write systems that are better contracted in contrast to systems just using plain values.

### The SI (système international d'unités; International System of Units)

SI's 7 base units and 2 supplementary units

Dimension Unit
SI base units:
Length (L) m (meter)
Mass (M) kg (kilogram)
Time (T)
s (second)
Electric Current (I) A (Ampère)
Thermodynamic Temperature (Theta) K (Kelvin)
Amount of Substance (N)
mol (mole)
Luminous Intensity (J)
cd (candela)
SI supplementary units:
Solid Angle (Omega)

The SI is an internationally agreed system of coherent units. It declares 7 base dimensions and exactly one unit per base dimension as a base unit, the core reference of scale in that dimension. No other (obsolete) unit should be used where SI units exist. A prominent example is meter , which is the SI base unit of base dimension length (L). Inch , mile, etc. are deprecated non-SI units. Base dimensions are orthogonal, in the sense that no base dimension can be expressed as a multiplicative combination of other base dimensions. Units can be prefixed by a set of standard multipliers (e.g. centi for10-2 or mega for 106).

Derived units are constructed as combinations of base units. Force for example has a derived dimension (M * L * T-2) whose unit kg * m * s-2 may also be written as N (Newton) .

There are two additional supplementary dimensions in the SI: the dimensions of plane and solid angles. The commitee seems not to have agreed on the nature of angular units. Even though there's little reason not to get angular units in the circle of base units, they are not "official" units. The mere fact that angular dimensions are often omitted is no justification for dropping them altogether. It is highly questionable whether we should really "merge" (meaning "confuse") the two fairly different notions of angle (a dimension with base unit rad) and dimensionless ratio ( angle / angle_of_full_revolution or length_of_circle_arc / circle_radius ). Therefore, EiffelUnits treats angular units like normal base units.

## Usage

Cluster "examples" shows common usage patterns of EiffelUnits. Cluster "test" contains some test classes.

### Client

Clients use the units and dimensions supplied with EiffelUnits. If your application stays within the SI unit system, it's likely that all the units you need are provided ready to use. Cluster unit.units holds classes with base dimensions and common units in these dimensions or powers thereof (e.g.m (meter) and l (liter) in class LENGTH, or s (second) and Hz (Hertz) in class TIME). Class COMPLEX_DERIVED provides some derived units with special names like N (Newton).

Class STANDARD_UNITS is a convenience class which includes all predefined units. The easiest way for clients to get access to EiffelUnits is by inheriting from STANDARD_UNITS. Another possibility (which doesn't clutter the namespace of your classes) is to define an attribute of type STANDARD_UNITS and access the units via a qualified call. Note that all classes defining units and dimensions are expanded. This allows clients to access their once-functions via an attribute without the need to create an object.

 ```class MY_CLASS inherit     STANDARD_UNITS feature     ... -- can directly use units``` ```class MY_CLASS feature     unit: STANDARD_UNITS     ... -- use units via `unit'.***```

The primary notion for calculations with units is class QUANTITY. Enities representing quantities are declared using type QUANTITY:

 ```altitude: QUANTITY speed: QUANTITY```

A QUANTITY object contains a DOUBLE value and a dimensionality. Quantities with equal dimensionality are said to be commensurate. Only commensurate quantities can be added, subtracted and compared to each other. These commensurateness conditions can be stated in contracts as shown here:

 ```feature     increase_altitude (by: QUANTITY) is         require             by.commensurate_with (m)         deferred         end invariant     altitude.commensurate_with (m)```

QUANTITY is an expanded class whose entities can hold any unit or quantity. Yes, that's right: units are not special objects. They are just quantities which happen to carry some dimension. You usually don't have to "create" a unit for your quantity from scratch. Instead, you use once functions from classes in cluster unit.units and employ them as factors in multiplications and divisions with other quantity objects. Class QUANTITY_CONVERSION(which is an ancestor of STANDARD_UNITS, but can also be used directly) provides feature `quantity (value: DOUBLE): QUANTITY` to create dimensionless quantities:

 ```feature     lightspeed: QUANTITY is         once             Result := quantity (299792458) * m / s         end```

Temperatures in units other than Kelvin require special treatment. Instructions are given in class THERMODYNAMIC_TEMPERATURE.

Commensurability between quantities is defined as equality of their dimensions. Therefore, every QUANTITY has a DIMENSION attached, which is accessible through feature dimension. Class STANDARD_DIMENSIONS holds a set of commonly used dimensions, including the SI base dimensions. An instance of class STANDARD_DIMENSIONS is available via feature dim from class QUANTITY_CONVERSION, which you will most likely inherit if you intend to use EiffelUnits.

#### Case Study: Atwood's Machine

(This example is taken from .) Atwood's Machine is a simple mechanical device which consists of two unequal masses suspended by a string from a massless, frictionless pulley. The upward acceleration a of mass m1is given by

a = (m2 - m1) / (m2 + m1)

and the tension t in the string is given by

t = 2 * g * m1 * m2 / (m1 + m2)

where g is the magnitude of the acceleration due to gravity. These simple formulas are incorporated into the following class for Atwood's machine:

 ```class     ATWOODS_MACHINE inherit     MASS     TIME     LENGTH     create     make     feature {NONE} -- Initialization     make (mass_1_: QUANTITY; mass_2_: QUANTITY) is             -- Load the machine         require             is_mass_1: mass_1_.commensurate_with (kg)             is_mass_2: mass_2_.commensurate_with (kg)         do             mass_1 := mass_1_             mass_2 := mass_2_         end feature -- Access     mass_1: QUANTITY     mass_2: QUANTITY         acceleration_1: QUANTITY is             -- acceleration of mass 1         do             Result := (mass_2 - mass_1) / (mass_1 + mass_2) * Gravity         ensure             Result.commensurate_with (m * s^-2)         end     tension: QUANTITY is             -- tension in the string         do             Result := quantity (2) * (mass_1 * mass_2) / (mass_1 + mass_2) * Gravity         ensure             Result.has_dimension (dim.Force)         end feature -- Constants     Gravity: QUANTITY is             -- gravitational acceleration on earth surface         once             Result := quantity (9.81) * m / (s ^ 2)         ensure             Result.has_dimension (dim.Acceleration)         end invariant     mass_1: mass_1.commensurate_with (kg)     mass_2: mass_2.has_dimension (dim.Mass) end -- class ATWOODS_MACHINE```

### Extension

If you need a derived dimension which is not defined in class STANDARD_DIMENSIONS, you can easily create it by multiplying and dividing base dimensions or existing derived dimensions. Remember that derived dimensions do not have to be "created" prior usage. They are generated on-the-fly as you execute calculations with existing units.

Sometimes, you need a new dimension for your special field of application. In this case, you simply create a new DIMENSION object, which becomes a new base dimension (orthogonal to all previous dimensions):

 ```class MY_CLASS inherit     QUANTITY_CONVERSION feature     My_dimension: DIMENSION is         once             create Result.make_new_base_dimension         end           my_base_unit: QUANTITY is         once             Result := base_quantity (My_dimension)         end ```

#### New Units: Case Study "Astronomical Units"

See class LENGTH_ASTRONOMY in cluster root_cluster.examples to see how you could provide additional units or constants.

#### New Dimensions: Case Study "Fuel Consumption"

This example shows how to extend EiffelUnits. New base dimensions Gasoline and Distance are created to aid in consistency checks. New units l_gasoline and m_distance are defined on top of the new dimensions.

 ```expanded class     FUEL_CONSUMPTION inherit     LENGTH         feature -- Base Dimensions     Gasoline: DIMENSION is         once             create Result.make_new_base_dimension         end             Gasoline_volume: DIMENSION is         once             Result := Gasoline * dim.Volume         end             Distance: DIMENSION is         once             create Result.make_new_base_dimension             Result := Result * dim.Length         end             Fuel_consumption: DIMENSION is         once             Result := Gasoline_volume / Distance         end feature -- Units     l_gasoline: QUANTITY is         once             Result := base_quantity (Gasoline) * l         ensure             definition: Result.is_equal (base_quantity (Gasoline) * l)             Result.has_dimension (Gasoline_volume)         end             km_distance: QUANTITY is         once             Result := quantity (1000) * base_quantity (Distance)         ensure             definition: Result.is_equal (quantity (1000) * base_quantity (Distance))             Result.has_dimension (Distance)         end end -- class FUEL_CONSUMPTION `````` indexing     description: "A Car with an average fuel consumption" class CAR inherit     FUEL_CONSUMPTION create     make feature -- Initialization     make (consumption_: QUANTITY) is             -- Set fuel consumption to `consumption_'         require             consumption_.has_dimension (Fuel_consumption)         do             consumption := consumption_         ensure             consumption_set: consumption = consumption_         end feature -- Access         km_for_tank (tank_contents: QUANTITY): QUANTITY is             -- how far can current car go?         require             tank_contents.commensurate_with (l_gasoline)         do             Result := tank_contents / consumption         ensure             Result.commensurate_with (km_distance)         end feature -- Status report     consumption: QUANTITY         -- average fuel consumption invariant     consumption.has_dimension (Fuel_consumption) end -- class CAR```

## Problems

Units are not statically checked. Unfortunately it was not possible to implement unit checking statically (at compile time). In complex unit expressions, we have to deal with a great many unforeseeable compound units. It is not feasible to manually provide classes and operations for all possible combinations of units. Furthermore, that approach would not be extensible.
In Eiffel, types are based either directly on classes or on generic derivations of generic classes. Since types are not computable on demand at compile time, I don't see a possibility to implement static unit checking. Please inform me if you disagree and have a solution. But remember: we don't want preprocessors or compiler hacks. Just a clean Eiffel library.
The increased safety gained with unit checking is currently traded in for a runtime overhead over unchecked programs.

Persistence of quantities is not supported. Base dimensions are currently identified in once functions by a monotonous increasing counter. Since the execution sequence of once functions is not neccessarily the same in different sessions, dimensions are not guaranteed to be assigned the same identifiers between runs. Therefore, quantity objects can not be stored and retrieved safely between different executions of a program by means of STORABLE.
A possible solution: identify a base dimension by its symbol (e.g. "L" for length), stored in some global place. But that would not statically ensure uniqueness.

EiffelUnits is based on ISE EiffelBase. I apologize for any inconveniences in terms of portability.

All values are DOUBLE. EiffelBase has weak support for generic usage of numeric data types. INTEGER and DOUBLE both inherit independently from NUMERIC and from COMPARABLE. Hence there's no common ancestor that clients can use if they don't care about the exact data type (be that INTEGER, DOUBLE, RATIONAL, BIG_INTEGER, etc.). The following class declaration is not legal in Eiffel: `class QUANTITY [G -> {NUMERIC, COMPARABLE}] inherit ...`

Conversion of DOUBLE value to QUANTITY. At the time of writing, there's no automatic object conversion in Eiffel. Therefore, you have to use feature `quantity (v: DOUBLE): QUANTITY` from class`QUANTITY_CONVERSION` to make the transition from (manifest) numbers to quantities. The reverse way is feature `in (unit: QUANTITY): DOUBLE` from class `QUANTITY`.

Known Bugs. There are no known bugs at this time. If you find one, please crunch it and send it to me. Thank you.

## Glossary

Base dimensions:
A set of dimensions (physical properties) which are assumed to be mutually independent. A base dimension can not be expressed by any combination of the other base dimensions. The base dimensions of the SI are listed in the table above.

Derived dimension:
A dimension that results from a product or quotient of two or more base dimensions. "Derived" is the nomenclature specified by the SI.

Dimension:
A measurable or calculable physical property, for example energy. SI uses the term "quantity" for this concept. A dimension may be a base dimension or a derived dimension.

Quantity (dimensioned quantity):
A numeric value together with its dimension.

Unit:
A Quantity which defines a reference of scale for measurements in a certain dimension. SI specifies one base unit for each base dimension. Certain derived dimensions have a base unit too (e.g. dimension force has base unit N, which stands for m * kg * s-2).

## References

 John J. Barton and Lee R. Nackman: "Scientific and engineering C++: an introduction with advanced techniques and examples". Reading, Massachusetts, Addison-Wesley, 1994.

Further information on units of measurement and standards can befound here: