3.2. Units references

A “units reference” is an attribute value that specifies the physical units a variable or number is in.

  1. A units reference SHALL be a CellML identifier.

  2. The units identified by a units reference SHALL be determined as follows:

    1. If the units reference is identical to a value in the “Name” column of Table 3.1: Built-in units, then it SHALL refer to the built-in units from the same row of the table.

    2. If the units reference is identical to the value of the name attribute of a units element in the same infoset, then it SHALL refer to the units specified by that element.

    3. If the units reference is identical to the value of the name attribute of an import units element in the same infoset, then it SHALL refer to units from the infoset defined by the import units element (see 3.1 Interpretation of import elements).

      1. The units specified SHALL then be determined by treating the value of the units_ref attribute on the import units element as a units reference within the imported infoset.

      2. If necessary, this rule SHALL be applied recursively.

  3. If no units can be identified using the rules above, the attribute value SHALL NOT be a valid units reference.

See more

Understanding units references

Units references are different from other reference types in that their value refers either to other units which you’ve imported or created in your model, or to one of the list of built-in units.

The trickier part is understanding them during an import process, where the scope or domain in which named units exist becomes important, as alluded to in points 3.2.1 and 3.2.2.2.

Consider the example below. The first model BlueberryPieRecipe simply combines two pre-made component ingredients; one for the crust and one for the filling.

model: BlueberryPieRecipe
  ├─ component: crust <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐
  └─ component: filling <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐ ╷
                                          ╷ ╷
                            imported components
                                          ╵ ╵
# In filling_recipes.cellml:              ╵ ╵
model: PieFillingRecipes                  ╵ ╵
  ├─ component: BlueberryCinnamonFilling ╶┘ ╵
  └─ component: AppleAndPearFilling         ╵
                                            ╵
# In crust_recipes.cellml:                  ╵
model: PieCrustRecipes                      ╵
  ├─ component: HazelnutLavenderCrust ╴╴╴╴╴╴┘
  └─ component: CheeseAndAlmondCrust

See CellML syntax

<!-- Inside the file "how_to_make_blueberry_pie.cellml": -->
<model name="BlueberryPieRecipe">
    <import xlink:href="relative/path/to/my/crust_recipes.cellml">
        <component name="crust" component_ref="HazelnutLavenderCrust" />
    </import>
    <import xlink:href="relative/path/to/my/filling_recipe.cellml">
      <component name="filling" component_ref="BlueberryCinnamonFilling" />
    </import>
</model>

The components are imported from separate files, each of which defines and uses its own local definitions of the custom measurement units spoon, dash, and smidgen.

  model: BlueberryPieRecipe
    ├─ component: crust <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐
    └─ component: filling <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐ ╷
                                                  ╷ ╷
                                    imported components and
                                        the units they need
                                                  ╵ ╵
    # In filling_recipes.cellml:                  ╵ ╵
    model: PieFillingRecipes                      ╵ ╵
      ├─ component: BlueberryCinnamonFilling ╴╴╴┬┬┘ ╵
      │    ├─ variable: blueberries (gram)      ╷╷  ╵
      │    ├─ variable: sugar (dimensionless)  units are implicitly
      │    ├─ variable: cornflour (gram)       imported by the component
      │    ├─ variable: cinnamon                ╵╵  ╵
  ┌╴╴╴╴╴╴╴╴╴> └─ units: smidgen ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╵  ╵
  ╷   |   └─ variable: water                     ╵  ╵
  ╷┌╴╴╴╴╴╴╴╴╴> └─ units: spoonful ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘  ╵
  ╷╷  |                                             ╵
  ╷╷  ├─ component: AppleAndPearFilling             ╵
  ╷╷  |                                             ╵
local custom units                                  ╵
  ╵╵  |                                             ╵
  ╵└╴╴├─ units: spoonful                            ╵
  └╴╴╴└─ units: smidgen                             ╵
                                                    ╵
    # In crust_recipes.cellml:                      ╵
    model: PieCrustRecipes                          ╵
      ├─ component: HazelnutLavenderCrust ╴╴╴╴╴╴╴┬┬┬┘
      │     ├─ variable: ground_hazelnut (gram)  ╷╷╷
      │     ├─ variable: egg (dimensionless)     ╷╷╷
      │     ├─ variable: flour (gram)         units are implicitly
      │     ├─ variable: sugar (gram)         imported by the component
      │     ├─ variable: water                   ╵╵╵
  ┌╴╴╴╴╴╴╴╴╴╴> └─ units: spoonful ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╵╵
  ╷   │     ├─ variable: salt                     ╵╵
  ╷┌╴╴╴╴╴╴╴╴╴> └─ units: dash ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘╵
  ╷╷  │     └─ variable: lavender_flowers          ╵
  ╷╷┌╴╴╴╴╴╴╴╴> └─ units: smidgen ╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┘
  ╷╷╷ │
local custom units
  ╵╵╵ │
  ╵╵╵ ├─ component: CheeseAndAlmondCrust
  ╵╵╵ │
  ╵╵└╴├─ units: smidgen
  ╵└╴╴├─ units: dash
  └╴╴╴├─ units: spoonful
      └─ units: dollop

See CellML syntax

<!-- Inside the file "crust_recipes.cellml": -->
<model name="PieCrustRecipes">

  <component name="HazelnutLavenderCrust">

    <!-- These units are built-in so do not change. -->
    <variables name="ground_hazelnut" units="gram" />
    <variables name="egg" units="dimensionless" />
    <variables name="flour" units="gram" />
    <variables name="sugar" units="gram" />

    <!-- These units are defined for this, their local scope, below. -->
    <variables name="water" units="spoonful" />
    <variables name="salt" units="dash" />
    <variables name="lavender_flowers" units="smidgen" />
    ...
  </component>

  <component name="CheeseAndAlmondCrust">
    ...
  </component>

  <!-- Local units definitions for spoonful, dash, and smidgen. -->
  <units name="spoonful">
    <unit units="litre" prefix="milli" multiplier="15" />
  </units>
  <units name="dash">
    <unit units="gram" multiplier="5" />
  </units>
  <units name="smidgen">
    <unit units="gram" multiplier="1" />
  </units>
  <units name="dollop">
    <unit units="litre" prefix="milli" multiplier="20" />
  </units>
</model>

<!-- Inside the file "filling_recipes.cellml": -->
<model name="PieFillingRecipes">

  <component name="BlueberryCinnamonFilling">
    <!-- These units are built-in, so do not change.  -->
    <variables name="blueberries" units="gram" />
    <variables name="sugar" units="dimensionless" />
    <variables name="cornflour" units="gram" />

    <!-- These units are defined for use in this, their local scope, below. -->
    <variables name="cinnamon" units="smidgen" />
    <variables name="water" units="spoonful" />

    <math>
        ...
    </math>
  </component>

  <component name="AppleAndPearFilling">
    ...
  </component>

  <!-- Local units definitions for spoonful and smidgen. -->
  <units name="spoonful">
    <unit units="litre" prefix="milli" multiplier="5" />
  </units>
  <units name="smidgen">
    <unit units="gram" multiplier="20" />
  </units>

</model>

This is where the idea of scope becomes important. As it stands, there is no conflict between the two different definitions of spoonful and smidgen, because each of the components refers to its own definition of these units. The components do not “know” that there is any other definition out there, because they cannot “see” up into the importing model.

Now let’s consider that the cook wants to alter the recipe a little after these two main ingredients have been imported, by adding a spoonful of brandy to some custard. The top-level model becomes:

model: BlueberryPieRecipe
  ├─ component: BrandyCustard
  │    ├─ variable: custard (litre)
  │    └─ variable: brandy
  │         └─ units: spoonful  # These units are not defined in a scope
  │                               which this component can access:
  │                               the model is invalid.
  ├─ component: crust <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐
  └─ component: filling <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐ ╷
                                          ╷ ╷
                            imported components
                                          ╵ ╵
# In filling_recipes.cellml:              ╵ ╵
model: PieFillingRecipes                  ╵ ╵
  ├─ component: BlueberryCinnamonFilling ╶┘ ╵
  └─ component: AppleAndPearFilling         ╵
                                            ╵
# In crust_recipes.cellml:                  ╵
model: PieCrustRecipes                      ╵
  ├─ component: HazelnutLavenderCrust ╴╴╴╴╴╴┘
  └─ component: CheeseAndAlmondCrust

See CellML syntax

<!-- Inside the file "how_to_make_blueberry_pie.cellml": -->
<model name="BlueberryPieRecipe">
  <import xlink:href="relative/path/to/my/crust_recipes.cellml">
    <component name="premade_crust" component_ref="HazelnutLavenderCrust" />
  </import>
  <import xlink:href="relative/path/to/my/filling_recipe.cellml">
    <component name="yummy_filling" component_ref="BlueberryCinnamonFilling" />
  </import>

  <!-- Defining a new component, brandy custard -->
  <component name="BrandyCustard">
    <variable name="custard" units="litre" />
    <variable name="brandy" units="spoonful" />
    ...
  </component>
</model>

At this stage the model is invalid because the units spoonful in the top-level model are not defined. Just as the imported models cannot “see” up into the importing model, neither can the importing model “see” down into the imported models beyond those items which it has explicitly imported.

In order to reuse the spoonful units from either of the imported models, they must be explicitly imported. The top-level model becomes:

  model: BlueberryPieRecipe
    ├─ component: BrandyCustard
    │    ├─ variable: custard (litre)
    │    └─ variable: brandy
┌╴╴╴╴╴╴╴╴> └─ units: spoonful
╷   ├─ component: crust <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐
╷   ├─ component: filling <╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴┐ ╷
╷   │                                     ╷ ╷
└╴╴╴└─ units: spoonful <╴╴╴╴╴╴╴┐     imported components and
                               ╷     the units they need
            explicitly imported units     ╵ ╵
      are available to all components     ╵ ╵
                               ╵          ╵ ╵
# In filling_recipes.cellml:   ╵          ╵ ╵
model: FillingRecipeCollection ╵          ╵ ╵
  ├─ units: spoonful ╴╴╴╴╴╴╴╴╴╴┘          ╵ ╵
  ├─ component: BlueberryCinnamonFilling ╶┘ ╵
  └─ component: AppleAndPearFilling         ╵
                                            ╵
# In crust_recipes.cellml:                  ╵
model: PieCrustRecipes                      ╵
  ├─ component: HazelnutLavenderCrust ╴╴╴╴╴╴┘
  └─ component: CheeseAndAlmondCrust

See CellML syntax

<!-- Inside the file "how_to_make_blueberry_pie.cellml": -->
<model name="BlueberryPieRecipe">
  <import xlink:href="relative/path/to/my/crust_recipes.cellml">
    <component name="premade_crust" component_ref="HazelnutLavenderCrust" />
  </import>
  <import xlink:href="relative/path/to/my/filling_recipe.cellml">
    <component name="yummy_filling" component_ref="BlueberryCinnamonFilling" />
  </import>

  <!-- Defining a new component, brandy custard -->
  <component name="BrandyCustard">
    <variable name="custard" units="litre" />
    <variable name="brandy" units="spoonful" />
    ...
  </component>

  <!-- Explicitly importing the "spoonful" units from the "filling_recipes.cellml" file: -->
  <import xlink:href="relative/path/to/my/filling_recipe.cellml">
    <!-- The units are also called "spoonful" in this top-level scope. -->
    <units name="spoonful" component_ref="spoonful" />
  </import>
</model>

At this stage we have three sets of units all named “spoonful”. Since each is only accessible to its local components there is no conflict of definition or interpretation. Now that the units required in the new BrandyCustard component are defined within the same scope, the model becomes valid, and our dessert needs are satisfied once more.

Table 3.1 Built-in units
Name

Unit reduction tuple
multiplier⋅(base, exponent)

ampere

-

becquerel

(second, -1)

candela

-

coulomb

(second, 1), (ampere, 1)

dimensionless

-

farad

(kilogram, -1), (metre, -2), (second, 4), (ampere, 2)

gram

0.001⋅(kilogram, 1)

gray

(metre, 2), (second, -2)

henry

(kilogram, 1), (metre, 2), (second, -2), (ampere, -2)

hertz

(second, -1)

joule

(kilogram, 1), (metre, 2), (second, -2)

katal

(second, -1), (mole, 1)

kelvin

-

kilogram

-

litre

0.001⋅(metre, 3)

lumen

(candela, 1)

lux

(metre, -2), (candela, 1)

metre

-

mole

-

newton

(kilogram, 1), (metre, 1), (second, -2)

ohm

(kilogram, 1), (metre, 2), (second, -3), (ampere, -2)

pascal

(kilogram, 1), (metre, -1), (second, -2)

radian

(dimensionless, 1)

second

-

siemens

(kilogram, -1), (metre, -2), (second, 3), (ampere, 2)

sievert

(metre, 2), (second, -2)

steradian

(dimensionless, 1)

tesla

(kilogram, 1), (second, -2), (ampere, -1)

volt

(kilogram, 1), (metre, 2), (second, -3), (ampere, -1)

watt

(kilogram, 1), (metre, 2), (second, -3)

weber

(kilogram, 1), (metre, 2), (second, -2), (ampere, -1)