Understanding units

Some basic units have been defined and built into libCellML, others you can define by combining the built-in ones using scaling factors and exponents, or you can define your own from scratch if need be.

There are four different kinds of units used here: irreducible units, built-in units, derived or combination units, and custom irreducible units.

Irreducible units

The first are called irreducible because they represent the physical base quantities which cannot be further simplified:

  • length (metre)

  • time (second)

  • amount of a substance (mole)

  • temperature (kelvin)

  • mass (kilogram)

  • electrical current (ampere)

  • luminous intensity (candela)

  • non-dimensional (dimensionless)

These irreducible units can be used to create all other physically-based units by combining them using different exponents, multipliers, and prefixes.

Built-in units

Some of these combinations form our second type of units, the built-in units, these being common relationships which have been constructed from combinations of the irreducible units. The combinations can involve:

  • A scaling factor (the units millisecond is equivalent to second and a factor of 0.001);

  • A combination of units (a coulomb is a second multiplied by an ampere);

  • Powers of units (a hertz has a base of second with an exponent of -1); and

  • Any combination of the above.

A list of pre-existing built-in convenience units is shown in the Built-in Units table, along with their relationships to the irreducible units.

Combination or derived units

The third type of units are those combinations which users can define for themselves based on the built-in units, the irreducible units, any other units already created, or (see below) their own custom irreducible units.

For example, let’s say that you want to simulate the time variable, \(t\), in units of milliseconds. This isn’t one of the built-in units, so you’ll need to define it, but it’s easy to see that it’s based on the built-in second, but needs a scaling factor.

For convenience libCellML gives a variety of options for defining such scaling factors:

  • Either through the use of named prefixes which are listed on the Interpretation of Units page, eg: millisecond is second with prefix="milli";

  • By defining an integer or integer string as a prefix which represents the \(log_{10}\) of the scaling factor, eg: millisecond is second with prefix=-3 gives a scaling factor of \(10^{-3}=0.001\). NB: using an integer string like prefix="-3" gives the same result; and

  • By defining the scaling factor directly, as a multiplier, eg: millisecond is second with multiplier=0.001.

The overloaded argument option list is shown below for each language. Please check the API Units documentation for details.

void addUnit(const std::string &reference, const std::string &prefix, double exponent = 1.0,
            double multiplier = 1.0, const std::string &id = "");

void addUnit(const std::string &reference, Prefix prefix, double exponent = 1.0,
            double multiplier = 1.0, const std::string &id = "");

void addUnit(const std::string &reference, int prefix, double exponent,
            double multiplier = 1.0, const std::string &id = "");

void addUnit(const std::string &reference, double exponent, const std::string &id = "");

void addUnit(const std::string &reference);

Note that reference can be another unit name string or a StandardUnits enum, and prefix can be a string or an integer.

To create a Units item you need will follow the same basic steps as other entities: declare it, name it, define it, and then add it in. For example:

// Declare, name, and define a "millisecond" unit pointer.
auto ms = libcellml::Units::create("millisecond");

// The manner of specification here is agnostic: all three definitions are identical.
ms->addUnit("second", "milli");  // reference unit and built-in prefix
// OR
ms->addUnit("second", 1.0, -3);  // reference unit, multiplier, exponent
// OR
ms->addUnit("second", 1.0, 0, 0.001);  // reference unit, multiplier, exponent

Units can be defined based on one another as well. For example, after defining our millisecond units, we could then use this definition to define the per_millisecond units by simply including it with an exponent of -1:

// Define a per_millisecond unit based on millisecond^-1:
per_ms->addUnit(ms, -1.0);

Custom irreducible units

The final type of unit is a custom irreducible unit. While this is not common in purely physical models (all of the seven physical attributes are already included), for times when you’re modelling something non-physical (such as our numbers of sharks or fishes), you’re able to define your own. Here’s an example.

// Create a custom irreducible unit named "banana".
auto uBanana = libcellml::Units::create("banana");

// Note that when a UnitsPtr is defined with a name only (that is, without any
// calls to the addUnit(...) function), it is effectively irreducible.

// Create a new compound unit based on the "banana" unit above.
auto uBunchOfBananas = libcellml::Units::create("bunch_of_bananas");
u2->addUnit("banana", 5.0);  // include bananas^5 in the bunch_of_bananas unit