Units

Units are stored in a cellmlmanip.units.UnitStore. When a model is parsed, a UnitStore is created automatically.

The cellmlmanip.units module provides unit handling for CellML models, using the Pint unit library.

class cellmlmanip.units.UnitStore(store=None)[source]

Bases: object

Creates and stores units, and has functions for unit conversion.

Within a UnitStore store, units are represented as store.Unit objects, while numbers with units are represented as store.Quantity objects. Both classes are inherited from the pint package.

Each UnitStore has its own unique namespace for storing units.

By default, each UnitStore maintains an internal pint.UnitRegistry and units from different stores cannot be compared or interact with each other. To allow comparison and conversion between unit stores, an existing UnitStore can be passed in at construction time, so that the underlying registry will be shared. (This will allow units from different stores to interact, but the unit stores will maintain independent namespaces).

For example:

a = UnitStore()
b = UnitStore()

creates two separated unit stores, each with their own namespace, and without the option to compare units from a to units from b.

But:

a = UnitStore()
b = UnitStore(a)
c = UnitStore(b)

creates three unit stores, each with their own namespace, but with a single underlying registry for all three stores, allowing e.g. units from c to be converted to units from a.

Parameters:

store – An existing UnitStore to share a unit registry with.

class Unit

A pint.Unit class tied to this unit store.

class Quantity

A pint.Quantity class tied to this unit store.

add_unit(name, expression)[source]

Adds a unit called name to the unit store, as defined by the string expression.

For example:

add_unit('mm', 'meter / 1000')
Parameters:
  • name – A string name. Names must be unique and cannot overlap with CellML predefined units.

  • expression – An expression to define the new unit.

Returns:

The newly created Unit object.

add_base_unit(name)[source]

Add a new base unit.

Parameters:

name – A string name.

is_defined(name)[source]

Check if a unit with the given name exists.

get_unit(name)[source]

Retrieves the Unit object with the given name.

Parameters:

unit_name – string name of the unit

Returns:

A Unit object.

Raises:

KeyError – If the unit is not defined in this unit store.

format(unit, base_units=False)[source]

Returns a string representation of a unit.

Parameters:
  • unit – The Unit object to format.

  • base_units – If this optional argument is set to True the string will show the base units expansion of the given unit.

is_equivalent(unit1, unit2)[source]

Tests whether two units are equivalent.

Parameters:
  • unit1 – The first Unit object to compare.

  • unit2 – The second Unit object to compare.

Returns:

True if both units are equivalent, False otherwise.

evaluate_units(expr)[source]

Evaluates and returns the Unit a Sympy expression is in.

Parameters:

expr – the Sympy expression whose units to evaluate.

Returns:

The calculated Unit object.

Raises:

UnitError – if there are unit errors when evaluating the expression’s units.

get_conversion_factor(from_unit, to_unit)[source]

Returns the magnitude multiplier required to convert a unit to the specified unit.

Parameters:
  • from_unit – the Unit to be converted

  • to_unitUnit object into which the units should be converted

Returns:

the magnitude of the resulting conversion factor

convert(quantity, unit)[source]

Converts the given quantity to be in the given unit.

Parameters:
  • quantity – a Quantity to convert.

  • unit – a Unit to convert it to.

Returns:

the converted Quantity.

add_conversion_rule(from_unit, to_unit, rule)[source]

Adds a complex conversion rule for converting between incompatible units.

For example:

units = UnitStore()
uA = units.add_unit('uA', 'ampere * 1e-6')
pA = units.add_unit('pA', 'ampere * 1e-12')
uF = units.add_unit('uF', 'farad * 1e-6')
pF = units.add_unit('pF', 'farad * 1e-12')
cm2 = units.add_unit('cm2', 'centimetre ** 2')
uA_per_cm2 = uA_per_cm2 = units.add_unit('uA_per_cm2', 'uA / cm2')
uF_per_cm2 = units.add_unit('uF_per_cm2', 'uF / cm2')
A_per_F = units.add_unit('A_per_F', 'ampere / farad')

Cm = units.Quantity(12, pF)
Cs = units.Quantity(1.1, uF_per_cm2)

units.add_conversion_rule(uA, uA_per_cm2, lambda ureg, rhs: rhs * Cs / Cm)
units.add_conversion_rule(uA_per_cm2, A_per_F, lambda ureg, rhs: rhs / Cs)

# Test
print(units.convert(units.Quantity(1, pA), A_per_F))
print(units.convert(units.Quantity(1, uA_per_cm2), A_per_F))
Parameters:
  • from_unit – a Unit in the dimension to convert from

  • to_unit – a Unit in the dimension to convert to

  • rule – a function of two arguments (unit registry and value) converting a value in the dimensions of from_unit to the dimensions of to_unit

Note that the function does not need to convert exactly to/from the specified units, just to the correct dimensions. Pint will scale appropriate once that is done.

evaluate_units_and_fix(expr)[source]

Evaluates and returns the Unit a Sympy expression is in; but will also attempt to fix inconsistencies in expr and return an updated expression if needed.

Parameters:

expr – the Sympy expression whose units to evaluate.

Returns:

A tuple (units, new_expr) where units is the calculated Unit object, and new_expr is either expr or a copy made internally consistent.

Raises:

UnitError – if there are unfixable unit errors when evaluating the expression’s units.

convert_expression_recursively(expr, to_units)[source]

Generate a version of the given expression in the requested units.

Rather than assuming the expression is internally consistent (and hence just wrapping in a conversion factor) this will recursively traverse the expression tree and convert at each level as needed. Hence if the operands of internal expressions are in dimensionally consistent but not equal units, conversions will be applied as needed.

If the to_units are given as None, this method will only convert to ensure that expr is internally consistent. As a result, this method is suitable for use converting the RHS of assignment equations to the units desired by the LHS, if the Eq expression is passed in as expr and to_units is given as None.

The conversion strategy for each (sub-)expression depends on the operator:

  • for relational operators, all operands are converted to the units of the first operand

  • for Mul the operands can be in any units, and we convert the result if needed

  • for Add all operands are converted to the desired units (or the units of the first operand if no desired units are given)

  • for Pow the exponent must be dimensionless while the operand can be in any units, and we convert the result if needed

  • for trig functions, exp, log, etc. the operands have to have dimensionless units

  • for piecewise, the conditions must be dimensionless, and the pieces are set to the desired units (or the units of the first piece)

  • for derivatives, numbers, variables, etc. we just convert to the desired units

Parameters:
  • expr – the Sympy expression to convert

  • to_units – the desired units of the expression as a Unit object, or None if we don’t care or for converting an assignment expression.

Returns:

a Sympy expression in the desired units; the input expr if no conversion was needed.

Raises:

UnitError – if conversion is not possible, using a suitable subclass depending on the exact reason

class cellmlmanip.units.UnitCalculator(unit_store)[source]

Bases: object

Evaluates a Sympy expression to determine its units.

Note: only supports a subset of Sympy math.

Parameters:

unit_store – A UnitStore.

traverse(expr)[source]

Descends the Sympy expression and performs Pint unit arithmetic on sub-expressions.

NOTE: Sympy will raise exceptions if the expression is badly formed.

Parameters:

expr – a Sympy expression

Returns:

the quantity (i.e. magnitude(expression) * unit) of the expression

Raises:
convert_expression_recursively(expr, to_units)[source]

Helper method for UnitStore.convert_expression_recursively() which does the heavy lifting.

Returns:

a tuple (new_expr, was_converted, actual_units)

exception cellmlmanip.units.UnitError[source]

Bases: Exception

Base class for errors relating to calculating units.

add_context(expression, message)[source]

Add extra context to the error message.

Will append ('Context: ' + message).format(expression) to the error message.

Parameters:
  • expression – the wider expression within which a unit calculation error occurred

  • message – further details explaining the context of the error

exception cellmlmanip.units.UnexpectedMathUnitsError(expression, message='')[source]

Bases: UnitError

Invalid units error thrown when math encountered in an expression is outside the subset of MathML expected.

Parameters:
  • expression – input expression in which the error occurred

  • message – optional message with further detail

exception cellmlmanip.units.InputArgumentsInvalidUnitsError(expression)[source]

Bases: UnitError

Invalid units error thrown when the arguments to a function have incorrect units.

For example it is incorrect to add 1 [meter] to 2 [seconds].

Parameters:

expression – input expression in which the error occurred

exception cellmlmanip.units.InputArgumentsMustBeDimensionlessError(expression, position='')[source]

Bases: UnitError

Invalid units error thrown when the arguments to a function have units when the function expects the arguments to be dimensionless.

For example it is incorrect to use a sine function on an argument with units.

Parameters:

expression – input expression in which the error occurred

exception cellmlmanip.units.InputArgumentMustBeNumberError(expression, position)[source]

Bases: UnitError

Invalid unit error thrown when the input argument should be a number.

For example root(x, y) is invalid unless y is a dimensionless number.

Parameters:
  • expression – input expression in which the error occurred

  • position – the position of the argument with error i.e. first/second

exception cellmlmanip.units.BooleanUnitsError(expression)[source]

Bases: UnitError

Invalid units error when being asked for units of an expression that will return a boolean.

For example it is incorrect to use the expression 1 [meter] > 0.5 [seconds].

Parameters:

expression – input expression in which the error occurred

exception cellmlmanip.units.UnitConversionError(expression, from_units, to_units)[source]

Bases: UnitError

Represents failure to convert between incompatible units.

Parameters:
  • expression – the Sympy expression in which the error occurred

  • from_unit – the units the expression is in

  • to_unit – the units we tried to convert to