Units and Scaling

The scaling module provides units and scaling capabilities.

Creating Quantities

underworld3.quantity(value, units=None)[source]

Create a unit-aware quantity.

Parameters:
  • value (float, int, or array-like) – The numerical value

  • units (str, optional) – Units specification (e.g., “Pa*s”, “cm/year”, “K”)

Returns:

Unit-aware quantity

Return type:

UWQuantity

Examples

>>> viscosity = uw.quantity(1e21, "Pa*s")
>>> velocity = uw.quantity(5, "cm/year")
>>> dT = uw.quantity(1000, "K") - uw.quantity(273, "K")
underworld3.expression(name, *args, _unique_name_generation=False, **kwargs)

A SymPy Symbol that wraps a value for lazy evaluation.

UWexpression is a named symbolic placeholder. When used in SymPy expressions, it acts as a Symbol. At evaluation time, the wrapped value is substituted.

Key Design (Simplified 2025-11): - Inherits from Symbol for SymPy compatibility - Does NOT inherit from UWQuantity (expressions don’t have units themselves) - Units are discovered from the wrapped thing when needed - Arithmetic returns pure SymPy expressions

Symbol Disambiguation (2025-12): - Uses _hashable_content() override (like sympy.Dummy) for uniqueness - Clean display names without invisible whitespace hacks - Symbols with same name but different _uw_id are distinct

Parameters:
  • name (str) – LaTeX-style name for display (e.g., r”alpha”, r”rho_0”)

  • sym (any, optional) – The wrapped value. Can be: - A number - A UWQuantity (carries units) - Another UWexpression (nested lazy evaluation) - A SymPy expression

  • description (str, optional) – Human-readable description

Examples

>>> alpha = uw.expression(r"\alpha", uw.quantity(3e-5, "1/K"))
>>> rho0 = uw.expression(r"\rho_0", uw.quantity(3300, "kg/m^3"))
>>>
>>> # Symbolic multiplication
>>> product = rho0 * alpha  # Returns SymPy Mul
>>>
>>> # Wrap the product for lazy evaluation
>>> combo = uw.expression(r"\rho_0 \alpha", product)

Unit Conversion

underworld3.get_units(expression, simplify=False)[source]

Get the units of an expression or quantity.

This is the unified, public API for extracting units from any object type (variables, quantities, SymPy expressions, etc.). It replaces the previous units_of() function and the internal function.unit_conversion.get_units() for a clean, single API surface.

IMPORTANT: This function ALWAYS returns Pint Unit objects (or None), never strings. We accept strings for INPUT (user convenience), but always return Pint objects.

Args:

expression: Expression, quantity, or unit-aware object simplify: If True, simplify mixed units to base SI (default False).

Returns:

Pint Unit object or None if no units

Examples:
>>> velocity = uw.discretisation.MeshVariable("velocity", mesh, 2, units="m/s")
>>> units = uw.get_units(velocity)
>>> print(units)  # <Unit('meter / second')>
>>> # Mixed units from composite expressions
>>> th2 = uw.expression("th2", ((2 * kappa * t_now))**0.5)
>>> uw.get_units(th2)  # megayear ** 0.5 * meter / second ** 0.5
>>> uw.get_units(th2, simplify=True)  # meter (simplified!)
Parameters:

simplify (bool)

Return type:

Any | None

underworld3.convert_units(quantity, target_units)[source]

Convert quantity to different units.

This is the SINGLE SOURCE OF TRUTH for unit conversion in UW3. All .to() methods on unit-aware types should delegate to this function.

Handles: - UWQuantity → returns new UWQuantity with converted value - UWexpression → returns new UWexpression with converted value - UnitAwareArray → returns new UnitAwareArray with converted values - Pint Quantity → returns converted Pint Quantity

Args:

quantity: Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint) target_units: Target units for conversion (str or Pint Unit)

Returns:

Same type as input, converted to target units

Raises:

DimensionalityError: If units are not compatible for conversion NoUnitsError: If quantity has no units

Examples:
>>> velocity = uw.quantity(10, "m/s")
>>> velocity_kmh = uw.convert_units(velocity, "km/h")
>>> print(velocity_kmh)  # 36.0 [kilometer / hour]
>>> radius = uw.expression("r", uw.quantity(6370, "km"))
>>> radius_m = uw.convert_units(radius, "m")
>>> print(radius_m.value)  # 6370000.0
Parameters:

target_units (str | Any)

Return type:

Any

underworld3.to_base_units(quantity)[source]

Convert quantity to SI base units.

This is a convenience function that converts any unit-aware quantity to its SI base unit representation.

Args:

quantity: Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint)

Returns:

Same type as input, converted to SI base units

Raises:

NoUnitsError: If quantity has no units

Examples:
>>> velocity = uw.quantity(36, "km/h")
>>> velocity_si = uw.to_base_units(velocity)
>>> print(velocity_si)  # 10.0 [meter / second]
>>> radius = uw.expression("r", uw.quantity(6370, "km"))
>>> radius_si = uw.to_base_units(radius)
>>> print(radius_si.value)  # 6370000.0
Return type:

Any

underworld3.to_compact(quantity)[source]

Convert quantity to most human-readable unit representation.

This automatically chooses unit prefixes (kilo, mega, micro, etc.) to make the number more readable. For example, 0.001 km becomes 1 m.

Args:

quantity: Quantity to make compact (UWQuantity, UWexpression, UnitAwareArray, Pint)

Returns:

Same type as input, with compact units

Raises:

NoUnitsError: If quantity has no units

Examples:
>>> length = uw.quantity(0.001, "km")
>>> length_compact = uw.to_compact(length)
>>> print(length_compact)  # 1.0 [meter]
>>> big_length = uw.quantity(1e9, "m")
>>> big_compact = uw.to_compact(big_length)
>>> print(big_compact)  # 1.0 [gigameter]
Return type:

Any

Non-dimensionalisation

underworld3.non_dimensionalise(expression, model=None)[source]

Convert expression to non-dimensional form using model reference scales.

This function uses dimensional analysis to compute appropriate scaling factors from the model’s reference quantities, then divides the expression by those scales to produce dimensionless values. Dimensionality information is preserved to enable re-dimensionalization.

Protocol-based approach works with: - MeshVariable/SwarmVariable (via .non_dimensional_value() method) - UWQuantity objects (extracts dimensionality, computes scale) - UnitAwareArray (extracts dimensionality from units) - Plain numbers (pass through unchanged)

Args:

expression: Expression, quantity, or unit-aware object to non-dimensionalise model: Model instance with reference quantities (uses default if None)

Returns:

Non-dimensional value(s) with preserved dimensionality metadata

Raises:

NoUnitsError: If expression has no units and model has reference quantities ValueError: If model has no reference quantities

Examples:
>>> # With variables (uses existing method)
>>> velocity_var = MeshVariable("v", mesh, 2, units="m/s")
>>> nondim_v = non_dimensionalise(velocity_var)
>>> # With UWQuantity
>>> velocity_qty = uw.quantity(5.0, "cm/year")
>>> nondim_qty = non_dimensionalise(velocity_qty, model)
>>> # Result is dimensionless but remembers it was velocity
>>> # With plain number (no model reference quantities)
>>> plain_value = 2.5
>>> result = non_dimensionalise(plain_value)  # Returns 2.5
Return type:

Any

underworld3.dimensionalise(expression, target_dimensionality=None, model=None)[source]

Restore dimensional form to non-dimensional values using model reference scales.

This is the companion function to non_dimensionalise(). It multiplies dimensionless values by the appropriate reference scale to restore their dimensional form.

The function can operate in two modes: 1. Auto mode: Extract dimensionality from the expression itself (if preserved) 2. Explicit mode: Use provided target_dimensionality

Parameters:
  • expression (UWQuantity, UnitAwareArray, or number) – Non-dimensional value with preserved dimensionality metadata.

  • target_dimensionality (dict, optional) – Target dimensionality in Pint format, e.g. {'[length]': 1, '[time]': -1} for velocity. If None, uses dimensionality from the expression.

  • model (Model, optional) – Model instance with reference quantities. Uses default if None.

Returns:

Dimensional quantity with appropriate units.

Return type:

quantity

Raises:
  • ValueError – If no dimensionality information is available.

  • ValueError – If model has no reference quantities.

Examples

Auto mode – dimensionality preserved from non_dimensionalise():

>>> velocity_qty = uw.quantity(5.0, "cm/year")
>>> nondim_vel = non_dimensionalise(velocity_qty, model)
>>> dimensional_vel = dimensionalise(nondim_vel, model=model)

Explicit mode – specify dimensionality:

>>> plain_value = 2.5
>>> velocity_dimensionality = {'[length]': 1, '[time]': -1}
>>> velocity = dimensionalise(plain_value, velocity_dimensionality, model)
underworld3.is_nondimensional_scaling_active()[source]

Check if non-dimensional scaling is currently enabled.

Returns:

True if non-dimensional scaling is active, False otherwise

Return type:

bool

See also

use_nondimensional_scaling

Enable/disable scaling

Dimensional Analysis

underworld3.get_dimensionality(expression)[source]

Get the dimensionality of an expression or quantity.

Args:

expression: Expression, quantity, or unit-aware object

Returns:

Dimensionality representation (backend-specific) or None if no units

Examples:
>>> velocity = EnhancedMeshVariable("velocity", mesh, 2, units="m/s")
>>> dims = get_dimensionality(velocity)
>>> print(dims)  # [length] / [time]
Return type:

Any | None

underworld3.is_dimensionless(expression)[source]

Check if expression is dimensionless.

Return type:

bool

underworld3.has_units(expression)[source]

Check if expression has units.

Return type:

bool

underworld3.same_units(expr1, expr2)[source]

Check if two expressions have the same units.

Return type:

bool