The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Physics::Unit - Manipulate physics units and dimensions.

SYNOPSIS

  use Physics::Unit ':ALL';   # exports all util. function names

  # Define your own units
  $ss = new Physics::Unit('furlong / fortnight', 'ff');

  # Print the expanded representation of a unit
  print $ss->expanded, "\n";

  # Convert from one to another
  print 'One ', $ss->name, ' is ', $ss->convert('mph'), " miles per hour\n";

  # Get a Unit's conversion factor
  print 'Conversion factor of foot is ', GetUnit('foot')->factor, "\n";

DESCRIPTION

This module allows for the representation of physical quantities with encapsulated units.

A Unit is defined by three things: a list of names, a conversion factor, and a dimensionality vector. From the dimensionality, a type can be derived (usually).

There are two main types of Unit objects: named and anonymous. Named Unit objects are constant. Anonymous Unit objects, however, can dynamically change.

Objects of class Unit define units of measurement that correspond to physical quantities. This module allows you to manipulate these units, generate new derived units from other units, and convert from one unit to another. Each unit is defined by a conversion factor and a dimensionality vector.

The conversion factor is a floating point number that specifies how this unit relates to a reference unit of the same dimensionality.

The dimensionality vector holds a list of integers - each of which records the power of a base unit in this unit.

For example, consider the unit of speed, "miles per hour". This has a dimensionality of (Distance / Time), or of (Distance ^ 1 * Time ^ -1). So this unit's dimensionality vector has a 1 in the place corresponding to Distance, and a -1 in the place corresponding to Time.

The reference unit for speed is "meters per second" (since meter is the base unit corresponding to Distance, and second is the base unit corresponding to Time). Therefore, the conversion factor for the unit "miles per hour" is 0.44704, since 1 mph equals 0.44704 meters / sec.

Try this:

  print "One mph is ", GetUnit('mph')->factor, " meters / sec\n";

Units that have the same dimensionality can be compared, and converted from one to the other.

Units are created through the use of unit expressions, which allow you to combine previously defined named units in new and interesting ways. In the synopsis above, "furlong / fortnight" is a unit expression.

Types of Units

A Unit can have one or more names associated with it, or it can be unnamed (anonymous).

Named units are constant. This ensures that expressions used to derive other units will remain consistent. For example, consider the expression "miles per hour", which uses the unit name "hour" to create a new, derived unit. It is possible that the same expression is used multiple times during the life of a program to create new Unit objects. If the unit refered to by the name "hour" was allowed to change, then a new, derived unit could possibly be different from another unit derived with the same expression.

Anonymous units, however, can be changed.

Among named Units, there are three types: prefixes, base units, and derived units.

A prefix Unit is a special case Unit object that:

  * is dimensionless
  * has only one name

A prefix name can be used in unit expressions in a special manner. They can be used as prefixes to other unit names, with no intervening whitespace. For example, "kilo" is a commonly used prefix. It can appear as in the following unit expressions:

  kilogram
  kilomegameter

Prefixes are described more fully in the unit expressions section below.

A base unit is one that defines a new base dimension. For example, the unit meter is a base unit; it defines the dimension for Distance.

A derived unit is one that is built up from other named units from a unit expression.

The terms base dimension and derived dimension (or derived type) are sometimes used. Distance is an example of a base dimension. It is not derived from any other set of dimensional quantities. Speed, however, is a derived dimension (or derived type), corresponding to Distance / Time.

Unit Names

Unit names are not allowed to contain whitespace, or any of the characters ^, *, /, (, ). Case is not significant. Also, they may not begin with any sequence of characters that could be interpreted as a decimal number. Furthermore, these reserved words are not allowed as unit names: per, square, sq, cubic, squared, or cubed. Other than that, pretty much anything goes.

So, for example, each of these is a valid unit name:

  blather
  blather-hour
  ..splather!min_glub

But these are not:

  ^glub_glub   # contains invalid character ^
  .1foo        # '.1' looks a lot like a number
  123abc       # so does '123'

Unit Expressions

Unit Expressions allow you to create new unit objects from the set of existing named units. Some examples of unit expressions are:

  megaparsec / femtosecond
  kg / feet^2 sec
  square millimeter
  kilogram meters per second squared

The explicit grammar for unit expressions is defined in the implementation page.

The operators allowed in unit expressions are, in order from high to low precedence:

prefix

Any prefix that is attached to a unit name is applied to that unit immediately (highest precedence). Note that if there is whitespace between the prefix and the unit name, this would be the space operator, which is not the same (see below).

  square, sq, or cubic

square or cube the next thing on the line

  squared or cubed

square or cube the previous thing on the line

  ** or ^

exponentiation (must be to an integral power)

  space

any amount of whitespace between units is considered a multiplication

  *, /, or per

multiplication or division

Parentheses can be used to override the precedence of any of the operators.

Prefixes are special case units, whose names can be attached to beginning of other units, with no intervening whitespace. The Unit module comes with a rather complete set of predefined SI prefixes; see the Units by Type page.

The prefixes are allowed before units, or by themselves. Thus, these are equivalent:

  megaparsec
  mega parsec
  kilo kilo parsec
  kilo**2 parsec
  square kilo parsec

Note in the last example that square applies only to kilo, and not to parsec. That's because the square operator has higher precedence than the space.

Note, however, that the space operator has higher precedence than '*', '/', or 'per'. This means that units separated by only whitespace in the denominator of an expression do not need to be enclosed in parentheses. Thus

  meters / sec sec

is a unit of acceleration, but

  meters / sec*sec

is not. The latter is equivalent to just 'meters'.

Predefined Units

A rather complete set of units is pre-defined in the library, so it will probably be rare that you'll need to define your own. See the units by name page, or the units by type page for a complete list.

A pound is a unit of force. I was very much tempted to make it a unit of mass, since that is much more often the way it is used, but I have a feeling I would have had to take more guff for that than I'm prepared to. The everyday pound, then, is named 'pound-mass', 'lbm', 'lbms', or 'pounds-mass'.

However, I couldn't bring myself to do the same thing to all the other American units derived from a pound. Therefore, ounce, ton, long-ton, and hundredweight are all units of mass.

A few physical constants were defined as Unit objects. This list is very restricted, however. I limited them to physical constants which really qualify as universal, according to (as much as I know of) the laws of physics, and a few constants which have been defined by international agreement. Thus, they are:

  * c   - the speed of light
  * G   - the universal gravitational constant
  * eq  - elementary charge
  * em  - electron mass
  * u   - atomic mass unit
  * g0  - standard gravity
  * atm - standard atmosphere
  * re  - equatorial radius of the reference geoid
  * rp  - polar radius of the reference geoid
  * h   - Planck constant
  * Na  - Avogadro constant

Name Conflicts and Resolutions

A few unit names and abbreviations had to be changed in order to avoid name conflicts. These are:

Elementary charge - abbreviated 'eq' instead of 'e'

Earth gravity - abbreviated 'g0' instead of 'g'

point - there are several definitions for this term: * typography -- point * I define it to be exactly 1/72 of an inch * my dictionary (Webster's II New College Dictionary) defines it as 0.01384 inch, or 72.2543 points / inch. * The Convert::Units::Base module sez that it's commonly defined as 0.01383 inch, or 72.3066 points / inch. * The Postscript documentation defines it as exactly 1/72 in. * The Convert::Units::Base module documentation also sez "Other type systems consider it 1/72.27 inch, or 0.01383 inches, or 0.0148 inches. Outside of that context, a point may be 1/120 or 1/144 inch."

  * a unit of academic classwork, usually equal to to one hour of class work
    per week during one semester
  * 11 deg., 15 min. between any two adjacent markings on a mariner's
    compass
  * a unit of scoring in any of a very many games
  * a unit equal to one dollar, used to quote the current prices of
    commodoties or stocks
  * a unit equal to one percentage point, used in reference to ownership
  * 2 milligrams (or 0.01 carat) used by jewelers -- j-point

minute - * duration -- minute * arc -- arcminute

second - * duration -- second * arc -- arcsecond

pound - * as a unit of force -- pound, pound-force, pounds-force, pound-weight, lbf * as a unit of mass -- pound-mass, pounds-mass, lbm * another unit of mass -- troy-pound

ounce - * as a unit of mass -- ounce, ounce-force, ozf * as a unit of volume -- fluid-ounce, floz, fluidounce * another unit of mass - troy-ounce

gram - * as a unit of mass - gram * as a unit of force -- gram-weight, gram-force

Export Options

By default, this module exports nothing. You can request all of the utility functions to be exported as follows:

  use Physics::Unit ':ALL';

Or, you can just get specific ones. For example:

  use Physics::Unit qw( GetUnit ListUnits );

Class Data

$debug

Turning this on enables copious debugging information. This is a package global, not a file-scoped lexical. So it can be turned on like this

  BEGIN { $Physics::Unit::debug = 1; }

  use Physics::Unit;

$number_re

This is the regular expression used to parse out a number. It is here so that other modules can use it for convenience.

A (correct) regular expression for a floating point number, optionally in exponent form. This is hard to come by.

%reserved_word

A list of reserved words in Unit expressions.

%unit_by_name

A list of all known unit names. The value of the hash is a reference to the named unit object.

%prefix

A list of all the valid prefixes. The value of the hash is a reference to the unit object. Note that the names here are also in %unit_by_name.

These are special case unit names that can be attached to other units with no intervening spaces.

%prototype

A list of all the known types. The values of this hash are references to unit objects that exemplify these types. I.e., any other units that have the same type will have the same dimensionality as the example unit.

$NumBases

The number of base units.

@BaseName

The name of each of the base units. These names also appear in %unit_by_name.

PUBLIC UTILITY FUNCTIONS

InitBaseUnit()

  Physics::Unit::InitBaseUnit( type, name-list,
                               type, name-list, ... );

InitBaseUnit is used to define any number of new, fundamental, independent dimensional quantities. Each such quantity is represented by a Unit object, which must have at least one name. From these base units, all the units in the system are derived.

The library is initialized to know about nine base quantities. These quantities, and the base units which represent them, are:

  1.  Distance - meter
  2.  Mass - gram
  3.  Time - second
  4.  Temperature - kelvin
  5.  Current - ampere
  6.  Substance - mole
  7.  Luminosity - candela
  8.  Money - us-dollar
  9.  Data - bit

More base quantities can be added at run-time, by calling this function. The arguments to this function are in pairs. Each pair consists of a type name followed by a reference to an array. The array consists of a list of names which can be used to reference the unit. For example:

  InitBaseUnit('Beauty' => ['sarah', 'sarahs', 'smw']);

This defines a new basic physical type, called Beauty. This also causes the creation of a single new Unit object, which has three names: sarah, sarahs, and smw. The type Beauty is refered to as a base type. The Unit sarah is refered to as the base unit corresponding to the type Beauty.

After defining a new base unit and type, you can then create other units derived from this unit, and other types derived from this type.

InitPrefix()

  Physics::Unit::InitPrefix( name, number,
                             name, number, ... );

This function is used to define strings that can be used to prefix any other unit name. As with InitBaseUnit, the library is initialized to know about a fair set of prefixes, and more can be added at run-time.

If you desire to create a prefix at run-time, call InitPrefix with a list of name-value pairs, for example:

  InitPrefix('gonzo' => 1e100, 'piccolo' => 1e-100);

From then on, you can use those prefixes to define new units, as in:

  $beauty_rate = new Physics::Unit('5 piccolosarah / hour');

InitUnit()

  Physics::Unit::InitUnit( name-list, unit-def,
                           name-list, unit-def, ... );

This function creates one or more new named Units. This is called at compile time to initialize the module with all the predefined units. It may also be called by users at runtime, to expand the unit system. For example:

  InitUnit( ['chris', 'cfm'] => '3 piccolosarahs' );

creates another unit of type Beauty equal to 3 * 10-100 sarahs.

Both this utility function and the new class method can be used to create new, named Units. There are minor differences between these two. The new method only allows you to create one unit at a time, whereas the InitUnit function can be used to create a large set of units with one call. The other difference is that units created with InitUnit must have a name, whereas new can be used to create anonymous Unit objects.

In this function and in others, wherever an argument is specified as unit-def, you can use either a Unit object, a single unit name, or a unit expression. So, for example, these are the same:

  InitUnit( ['mycron'], '3600 sec' );
  InitUnit( ['mycron'], 'hour' );
  $h = GetUnit('hour');  InitUnit( ['mycron'], $h );

creates a new unit named mycron which is the same as one hour.

InitTypes()

  Physics::Unit::InitTypes( type-name, unit-def,
                            type-name, unit-def, ... );

Use this function to define derived types. For example:

  InitTypes( 'Aging' => 'chris / year' );

might describe the loss of beauty with time.

This function associates a type name with a specific dimensionality. The factor of the unit is not used. I.e., in the above example, Aging is associated with ( Beauty / Time ). The factor of the unit 'chris / year' is not used.

The unit-def argument can be a single unit name, a unit expression, or a Unit object.

GetUnit()

  $u = Physics::Unit::GetUnit( unit-def );

Returns a unit associated with the the argument passed in. The argument can either be a name, a unit expression, or a Unit object.

If the argument is a Unit object, it is simply returned. If the argument is a simple unit name, then this returns a reference to the named unit.

If the argument cannot be found as a simple unit name, then this method attempts to evaluate it as an expression. If it is successful, it will create a new, anonymous Unit object and return a reference to it.

For example:

  # This returns a reference to a pre-defined, named unit:
  $u = GetUnit('gram');

  # This creates a new, anonymous unit and returns a reference to it:
  $u = GetUnit('km / hour');

ListUnits()

  @l = Physics::Unit::ListUnits;

Returns a list of all unit names known, sorted alphabetically.

ListTypes()

  @l = Physics::Unit::ListTypes;

Returns a list of all the quantity types known to the library, sorted alphabetically.

NumBases()

  $n = Physics::Unit::NumBases;

Returns the number of base dimension units.

GetTypeUnit()

  $u = Physics::Unit::GetTypeUnit( type-name );

Returns the Unit object corresponding to a given type.

PUBLIC METHODS

new()

  $u1 = new Physics::Unit( unit-def [, name, name, ... ] );
  $u2 = $u1->new( [name, name, ... ] );

This method creates a new Unit object. The names are optional. (Note: the square brackets above are used to indicate that the name list is optional, not that the argument is a reference to an anonymous array. See the examples below.)

If names are given, then the new Unit will be given those names, and that object will thereafter be constant.

If more than one name is given, the first is the primary name. The primary name is retrieved whenever the name method is called.

If a unit has a name or names, those names must be different than every other unit name known to the library. See the Unit by Names page to see an alphabetical list of all the pre-defined unit names.

If no names are given, then an anonymous unit is created. Note that another way of creating new anonymous units is with the GetUnit utility function.

Examples:

  # Create a new, anonymous unit:
  $u = new Physics::Unit ('3 pi sarahs per s');

  # Create a new, named unit:
  $u = new Physics::Unit ('3 pi sarahs per s', 'bloom');

  # Or, create a new unit with a list of names:
  $u  = new Physics::Unit ('3 pi sarahs per s', 'b', 'blooms', 'blm');
  $n = $u->name;   # returns 'b'

  @@ - add a description, and an example of the use of this as an object
       method

type()

  $t = $u->type;
  $u->type( type-name );

Get or set this unit's type.

For example:

  GetUnit('rod')->type;    # returns 'Distance'

It will almost never be necessary to set a Unit object's type. The type is normally determined uniquely from the dimensionality of the unit. However, occasionally, more than one type can match a given unit's dimensionality. For example, Torque and Energy have the same dimensionality.

In that case, all of the predefined, named units are explicitly designated to be one type or the other. For example, the unit newton is defined to have the type Energy. See the list of units by type to see which units are defined as Energy and which as Torque.

However, if you are going to create new Unit objects from unit expressions that have that dimensionality, it will be necessary to explicitly specify which type that unit object is.

When this method is called to set the unit's type, only one type string argument is allowed, and it must be a predefined type name (see InitTypes above).

Once a single type has been associated with a unit, then that will remain that unit's type for the rest of the program, unless it is re-set again.

This method returns one of:

  undef

no type was found to match the unit's dimensionality

  'prefix'

in the special case where the unit is a named prefix

  type_name

the prototype unit for type_name matches the unit's dimensionality (see InitTypes above)

  ref to an array of type_name's

more than one type was found to match the unit's dimensionality

Some examples may perhaps make this clear:

  $u1 = new Physics::Unit('kg m^2/s^2');
  $t = $u1->type;       #  $t == ['Energy', 'Torque']

  $u1->type('Energy');  #  This establishes the type once and for all
  $t = $u1->type;       #  $t == 'Energy'

  # Now create another Unit object from the same definition
  $u2 = new Physics::Unit('kg m^2/s^2');

  # This is a brand-new object, so the explicit type is unknown, as before:
  $t = $u2->type;    # $t == ['Energy', 'Torque']

  # But if we use a predefined, named unit, we get a single type:
  $u3 = GetUnit('joule')->new;    # *not*  Physics::Unit->new('joule');
  $t = $u3->type;    # $t == 'Energy'

name()

  $n = $u->name;

Returns the primary name of the unit. If this unit has no names, then this method returns undef.

abbr()

  $a = $u->abbr;

Returns the shortest name of the unit. If this unit has no names, this method will return the undef.

names()

  @a = $u->names;

Returns a list of names that can be used to reference the unit. Returns undef if the unit is unnamed.

Be aware: this might be an empty list - whereas the name() method above will always return something meaningful.

def()

  $s = $u->def;

Returns the string that was used to define this unit. Note that if the unit has been manipulated with any of the arithmetic methods, then the def method will return undef, since the definition string is no longer a valid definition of the unit.

expanded()

  $s = $u->expanded;

Produces a string representation of the unit, in terms of the base units (see InitBaseUnit above).

For example:

  print GetUnit('calorie')->expanded, "\n";

produces

  4184 m^2 gm s^-2

ToString()

  $s = $u->ToString;

There are several ways to have a Unit object print itself to a string. This method is designed to give you what you usually want, and to be guaranteed to always print out something meaningful.

If the object is named, this does the same as the name method above. Otherwise, if the object's definition string is still valid, this does the same as the def method above. Otherwise, this does the same thing as the expanded method.

factor()

  $f = $u->factor;
  $u->factor(newvalue);    # $u must be unnamed

Get or set the unit's conversion factor. If this is used to set a Unit's factor, then the Unit object must be anonymous.

convert()

  $f = $u->convert( unit-def );

Returns the number which converts this unit to another. The types of the units must match. For example:

  $mile = GetUnit('mile');
  $foot = GetUnit('foot');
  $c = $mile->convert($foot);     # returns 5280

times()

  $u->times( unit-def );

$u is multiplied by either a Unit or a number. $u must be anonymous.

recip()

  $u->recip;

$u is replaced with its reciprocal. $u must be anonymous.

divide()

  $u->divide( unit-def );

$u is divided by either a Unit or a number, and the result replaces $u. $u must be anonymous.

For example:

  $u = new Physics::Unit('36 m^2');
  $u->divide('3 meters');    # $u is now '12 m'
  $u->divide(3);             # $u is now '4 m'
  $u->divide( new Physics::Unit('.5 sec') );  # $u is now '8 m/s'

power()

  $u->power( integer );

Raises a unit to an integral power. $u must be anonymous.

add()

  $u->add( unit );   # unit types must match

unit is added to $u. $u and unit must be of the same type. $u must be anonymous.

neg()

  $u->neg;

$u is replaced with its arithmetic negative. $u must be anonymous.

subtract()

  $u->subtract( unit-def );   # unit types must match

unit is subtracted from $u. $u and unit must be of the same type. $u must be anonymous.

copy()

  $n = $u->copy;

This creates a copy of an existing unit. It doesn't copy the names, however. So you are free to modify the copy (while modification of named units is verboten).

If the type of the existing unit is well-defined, then it, also, is copied.

This is the same as the new method, when new is called as an object method with no names.

equal()

  $u1->equal( unit-def );
  Physics::Unit->equal( unit-def, unit-def );

This returns 1 if the two unit objects have the same type and the same conversion factor.

EXPRESSION GRAMMAR

  expr : term
       | term '/' expr
       | term '*' expr
       | term 'per' expr

  term : factor
       | term factor

A term is any number of factors separated (nominally) by whitespace. Whitespace is an 'operator' that means the same thing as multiplication, but has a higher priority than either '*', '/', or 'per'.

Examples of terms (the following lines each contain one term):

  3pi radians
  3e+4 globules

  factor : prim
         | prim '**' integer

Note that a primary can be an integer, of course, so factors can look like this:

  meter ** 3 ^ 5    # note, '**' and '^' are synonymous

  prim : number
       | word
       | '(' expr ')'
       | 'square' primary
       | 'sq' primary
       | 'cubic' primary
       | primary 'squared'
       | primary 'cubed'

AUTHOR

Written by Chris Maloney <Dude@chrismaloney.com>

Formatted for distribution by Gene Boggs <cpan@ology.net>

COPYRIGHT AND LICENSE

Copyright 2002-2003 by Chris Maloney

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.