Model Management

Underworld3 Model Architecture

This module provides the Model class that serves as a central orchestrator for simulation components and object lifecycle management.

The Model object: - Eliminates circular references by becoming the central authority for mesh/swarms - Enables mesh swapping with automatic notification to dependent objects - Provides simple container for organizing simulation components - Supports serialization for model reuse and sharing

Design Philosophy: - Simple orchestration, not complex validation - Use existing sympy expressions, not separate parameter system - PETSc handles command-line options - Materials are just dictionaries of expressions/values

Model Class

class underworld3.Model[source]

Bases: PintNativeModelMixin, BaseModel

Central orchestrator for Underworld3 simulations.

Enhanced with Pydantic for validation, serialization, and configuration management while preserving the original design philosophy of simple orchestration.

The Model object manages: - Mesh and coordinate system lifecycle - Swarm registration and migration - Variable dependencies and updates - Parameter validation and propagation - Material definitions and assignments - Solver coordination

Benefits: - Eliminates circular references between components - Enables mesh swapping and dynamic reconfiguration - Provides single point for parameter management - Supports model composition and reuse - Enhanced serialization with YAML/JSON support - Optional validation with Pydantic

Example:
>>> model = uw.Model()
>>> model.set_mesh(mesh)
>>> swarm = model.create_swarm()
>>> temperature = model.add_variable("temperature", mesh, uw.VarType.SCALAR)
name: str
materials: Dict[str, Dict[str, Any]]
metadata: Dict[str, Any]
version: int
unit_rounding_mode: str
petsc_state: Dict[str, str] | None
model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'allow', 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

__init__(name=None, **kwargs)[source]

Initialize a new Model instance.

Parameters:
  • name (str, optional) – Human-readable name for this model instance

  • **kwargs (dict) – Additional arguments for Pydantic BaseModel

state: ModelState
property mesh

The primary mesh for this model

list_meshes()[source]

List all meshes registered with this model

Return type:

Dict[int, Any]

get_mesh(mesh_id)[source]

Get a mesh by ID from the model registry

Parameters:

mesh_id (int)

set_primary_mesh(mesh)[source]

Set a mesh as the primary mesh for this model.

Parameters:

meshuw.discretisation.Mesh

Mesh to set as primary (must already be registered)

set_mesh(mesh)[source]

Set or replace the primary mesh for this model.

This method handles: - Registering mesh if not already registered - Setting as primary mesh - Incrementing version for change tracking

Parameters:

meshuw.discretisation.Mesh

The new mesh to use as primary for this model

get_variable(name, mesh=None, swarm=None)[source]

Get a variable by name from the model registry.

Parameters:

namestr

Variable name to look up

meshMesh, optional

If provided, look for variable specifically on this mesh

swarmSwarm, optional

If provided, look for variable specifically on this swarm

Returns:

Variable or None

Parameters:

name (str)

get_qualified_name(variable)[source]

Get the fully qualified name for a variable.

Returns the name that uniquely identifies this variable in the model registry, which may include mesh/swarm ID qualifiers to resolve namespace conflicts.

Parameters:

variableMeshVariable or SwarmVariable

Variable to get qualified name for

Returns:

str or None : Qualified name if found, None otherwise

transfer_variable_data(source_var, target_var)[source]

Transfer data from source variable to target variable using global_evaluate.

This enables persistent variable identity across mesh changes by transferring data from variables on old meshes to variables on new meshes.

Parameters:

source_varMeshVariable or SwarmVariable

Source variable to transfer data from

target_varMeshVariable or SwarmVariable

Target variable to transfer data to

Returns:

bool : True if transfer successful, False otherwise

list_variables()[source]

List all variables registered with this model

Return type:

Dict[str, Any]

list_swarms()[source]

List all swarms registered with this model

Return type:

Dict[str, Any]

add_solver(name, solver)[source]

Register a solver with this model

Parameters:

name (str)

get_solver(name)[source]

Get a solver by name from the model registry

Parameters:

name (str)

property tracker

Snapshot-managed record of where this run is.

Holds time / step / dt (pre-seeded conventions) plus any quantity you assign — model.tracker.foo = .... Everything on the tracker is captured by snapshot() and reverted by restore(); loose Python variables are not. Solvers do not depend on it; using it is optional.

save_state(*, file=None)[source]

Save the model’s current state — memory or disk, one entry point.

Without file, captures an in-memory Snapshot token suitable for in-run backtracking (“stash for timesteps”). The token is plain Python / numpy — fast to produce, fast to restore, does not survive the process.

With file=<path>, writes a persistent on-disk snapshot at that path (plus a sibling .bulk/ directory holding the bulk PETSc + swarm sidecars). Survives the process; suitable for restart, postprocessing, transferring runs.

Either way the captured state is the full model: all registered meshes and mesh-variables, all swarms with per-particle data, all solver-internal state-bearers (ModelTracker, DDt instances, anything else exposing the Snapshottable contract).

Parameters:

file (str, optional) – Path to write a disk snapshot to. If omitted, an in-memory token is returned instead.

Returns:

  • Snapshot – When called without file — pass to load_state() to restore.

  • str – When file is given — the path the snapshot was written to (same as file).

load_state(source)[source]

Restore the model from a previously saved state — memory or disk.

source is either:

  • a Snapshot token returned by an earlier save_state() call (in-memory restore — bit-exact),

  • a path string to a disk-snapshot file (disk restore — same-rank, same-model contract; mesh-rebuild on read is v1.2 scope).

Raises:
  • TypeErrorsource is neither a Snapshot nor a string.

  • SnapshotInvalidatedError – The captured state no longer matches what is registered (mesh adapted, state-bearer missing, …).

Return type:

None

define_parameter(name, ptype=None, **kwargs)[source]

Define a new parameter with validation rules.

NOTE: Parameter system not yet implemented. Use model.materials dict directly.

Parameters:
  • name (str) – Parameter path (e.g., ‘material.viscosity’, ‘solver.tolerance’)

  • ptype (ParameterType, optional) – Parameter type for validation (not used yet)

  • **kwargs (dict) – Additional arguments

set_material(name, properties)[source]

Set material properties (simple dictionary).

Parameters:

namestr

Material name (e.g., ‘mantle’, ‘crust’, ‘plume’)

propertiesdict

Dictionary of property_name -> value/expression

Example:

>>> model.set_material('mantle', {'viscosity': 1e21, 'density': 3300})
Parameters:
get_material(name)[source]

Get material properties by name

Parameters:

name (str)

set_reference_quantities(verbose=False, nondimensional_scaling=True, **quantities)[source]

Set reference quantities for automatic units scaling.

This method enables the hybrid SymPy+Pint units workflow by allowing users to specify their problem in natural units, from which the system derives optimal scaling for numerical conditioning.

By default, this automatically enables non-dimensionalization for solvers, ensuring consistent behavior between user-facing units and solver internals.

Parameters:
  • verbose (bool, optional) – If True, print diagnostic information about dimensional analysis and scale derivation. Default: False.

  • nondimensional_scaling (bool, optional) – Enable automatic non-dimensionalization for solvers (default: True). When True (recommended), solver operations work in non-dimensional [0-1] space while user-facing values remain in physical units. Set to False for expert mode (dimensional units only, no scaling). Disabling this may cause numerical conditioning issues.

  • **quantities (dict) – Named reference quantities using Pint units or UWQuantity objects, e.g. domain_depth=uw.quantity(2900, "km").

Raises:

RuntimeError – If called after a mesh has been created (units are locked)

Notes

This method creates a Pint-native registry with model-specific constants using the _constants pattern for optimal numerical conditioning.

The default behavior (nondimensional_scaling=True) ensures user-facing values in physical units, solver operations in well-conditioned non-dimensional [0-1] space, and automatic conversions.

get_reference_quantities()[source]

Get the reference quantities for this model.

property fundamental_scales

Access the derived fundamental scales for dimensional analysis.

Returns the canonical fundamental scaling quantities [L], [M], [T], [θ] that were automatically derived from the reference quantities via dimensional analysis. These scales are used throughout the system for automatic non-dimensionalization.

The system extracts these fundamental scales from user-provided reference quantities (which can have any domain-specific names) through pure dimensional analysis. This provides a domain-agnostic API independent of parameter naming.

Returns:

Dictionary mapping canonical dimension names to their scaling quantities: - ‘length’: Length scale [L] (e.g., 1000 m) - ‘time’: Time scale [T] (e.g., 1 Myr) - ‘mass’: Mass scale [M] (e.g., 1e24 kg) - ‘temperature’: Temperature scale [θ] (e.g., 1000 K) - Plus any additional dimensions (velocity, viscosity, pressure, etc.)

Return type:

dict

Example

>>> model = uw.get_default_model()
>>> model.set_reference_quantities(
...     domain_depth=uw.quantity(2900, "km"),
...     plate_velocity=uw.quantity(5, "cm/year"),
...     mantle_viscosity=uw.quantity(1e21, "Pa*s"),
...     temperature_difference=uw.quantity(3000, "K")
... )
>>> # Access derived fundamental scales (NOT the user-provided reference quantities)
>>> L0 = model.fundamental_scales['length']      # 2900 km
>>> T0 = model.fundamental_scales['time']        # ~1.8 Myr
>>> eta0 = model.fundamental_scales['viscosity'] # 1e21 Pa*s
>>> DeltaT = model.fundamental_scales['temperature']  # 3000 K

Notes

  • These are the ROUNDED fundamental scales stored internally after dimensional analysis

  • The scales may have been rounded to nice values for O(1) numerical conditioning

  • Use these scales for physical interpretation of non-dimensional solutions

  • Do NOT use user-provided reference quantity names (domain-specific)

has_units()[source]

Check if this model has units scaling enabled.

validate_reference_quantities(raise_on_error=False)[source]

Validate that all registered variables with units have required reference quantities.

This method checks each variable and reports any missing reference quantities needed to properly derive scaling coefficients.

Parameters:

raise_on_errorbool, optional

If True, raise ValueError on validation failure. If False (default), return validation results without raising.

Returns:

dict

Validation results with keys: - ‘valid’: bool - Overall validation status - ‘warnings’: list of warning messages - ‘errors’: list of error messages

Example:

>>> model = uw.get_default_model()
>>> model.set_reference_quantities(domain_depth=uw.quantity(1000, "km"))
>>> mesh = uw.meshing.StructuredQuadBox(...)
>>> v = uw.discretisation.MeshVariable('v', mesh, 2, degree=2, units='m/s')
>>> result = model.validate_reference_quantities()
>>> if not result['valid']:
...     print("\n".join(result['errors']))
get_coordinate_unit()[source]

Get the coordinate unit for this model.

Returns the length unit from reference quantities if set, otherwise returns None (dimensionless).

Returns:

pint.Unit or None

Coordinate unit object (e.g., <Unit(‘kilometer’)>) or None if no units set

Example:

>>> model.set_reference_quantities(domain_depth=uw.quantity(500, "km"))
>>> model.get_coordinate_unit()
<Unit('kilometer')>  # Or <Unit('megameter')> if using engineering rounding

Changed in 2025-10-16: Now returns pint.Unit objects instead of strings for consistency and to enable natural unit arithmetic (e.g., var.units / mesh.units).

set_as_default()[source]

Explicitly set this model as the default model.

This is useful when working with multiple models (e.g., loading from disk) and you want to explicitly control which model is active.

Returns:

Model

Returns self for method chaining

Example:

>>> model1 = uw.Model()
>>> model2 = uw.Model()
>>> model2.set_as_default()  # Make model2 the active default
>>> assert uw.get_default_model() is model2
get_unit_aliases()[source]

Get all available unit aliases for user reference.

Returns:

Dictionary mapping dimensions to their available aliases.

Return type:

dict

Examples

>>> model.get_unit_aliases()
{
    'length': {
        'ascii': ['L_ref', 'L_scale', 'L_model', 'length_scale'],
        'unicode': 'ℒ',
        'display': 'ℒ',
        'technical': '_1000km'
    },
    ...
}
derive_fundamental_scalings()[source]

Derive fundamental scaling units (length, time, mass, temperature) from reference quantities.

This method analyzes the dimensional structure of reference quantities to automatically determine optimal fundamental scalings for the problem domain.

Returns:

Dictionary of fundamental scalings with keys like ‘[length]’, ‘[time]’, ‘[mass]’, ‘[temperature]’

Return type:

dict

Examples

>>> model.set_reference_quantities(
...     mantle_viscosity=1e21*uw.units.Pa*uw.units.s,
...     plate_velocity=5*uw.units.cm/uw.units.year,
...     mantle_temperature=1500*uw.units.K
... )
>>> scalings = model.derive_fundamental_scalings()
>>> scalings['[length]']  # Derived from plate_velocity
>>> scalings['[time]']    # Derived from plate_velocity
>>> scalings['[temperature]']  # Direct from mantle_temperature
get_fundamental_scales()[source]

Get fundamental scales in user-friendly format.

Returns the derived fundamental scaling quantities (length, time, mass, temperature) that the model uses for dimensional analysis. These are the base scales from which all other quantities are converted to model units.

IMPORTANT: Returns the ROUNDED scales stored in _fundamental_scales, not re-derived. The scales may have been rounded to nice values for O(1) numerical conditioning.

Returns:

Dictionary mapping dimension names to their scaling quantities: - ‘length’: Length scale quantity (e.g., 1000 m, rounded from 500 m) - ‘time’: Time scale quantity (e.g., 1 Myr, rounded) - ‘mass’: Mass scale quantity (e.g., 1e11 kg, rounded) - ‘temperature’: Temperature scale quantity (e.g., 1000 K, rounded) - Plus any additional dimensions detected (current, substance, etc.)

Return type:

dict

Examples

>>> model.set_reference_quantities(
...     domain_depth=500*uw.units.m,  # Will round to 1000 m
...     plate_velocity=5*uw.units.cm/uw.units.year,
...     mantle_viscosity=1e21*uw.units.Pa*uw.units.s,
...     mantle_temperature=1500*uw.units.K
... )
>>> scales = model.get_fundamental_scales()
>>> scales['length']  # 1000 meter (rounded, not 500)
get_scale_for_dimensionality(dimensionality)[source]

Get appropriate reference scale for given dimensionality.

First checks if user explicitly provided a reference quantity with this exact dimensionality (e.g., diffusivity=1e-6 m²/s). If found, uses that directly. Otherwise, computes from fundamental scales (length, time, mass, temperature).

This design ensures that explicit user-provided scales take precedence over derived scales, which is important for physical accuracy. For example, thermal diffusivity (~1e-6 m²/s) is orders of magnitude different from L²/t derived from mantle convection length and time scales.

Parameters:

dimensionality (dict) – Pint dimensionality dictionary, e.g., {‘[length]’: 1, ‘[time]’: -1} for velocity, {‘[mass]’: 1, ‘[length]’: -1, ‘[time]’: -2} for pressure

Returns:

Reference scale with appropriate dimensionality

Return type:

pint.Quantity

Raises:

ValueError – If dimensionality requires scales not available in fundamental scales

Examples

>>> model.set_reference_quantities(domain_depth=uw.quantity(2900, "km"), ...)
>>> velocity_dim = {'[length]': 1, '[time]': -1}
>>> v_scale = model.get_scale_for_dimensionality(velocity_dim)
>>> # v_scale = 2900 km / (time_scale) in m/s
>>> pressure_dim = {'[mass]': 1, '[length]': -1, '[time]': -2}
>>> p_scale = model.get_scale_for_dimensionality(pressure_dim)
>>> # p_scale = M/(L·T²) in Pa
>>> # Explicit diffusivity takes precedence over L²/t
>>> model.set_reference_quantities(
...     length=uw.quantity(2900, "km"),
...     time=uw.quantity(1e15, "s"),
...     diffusivity=uw.quantity(1e-6, "m**2/s")  # Explicit!
... )
>>> diff_dim = {'[length]': 2, '[time]': -1}
>>> diff_scale = model.get_scale_for_dimensionality(diff_dim)
>>> # diff_scale = 1e-6 m²/s (explicit), NOT L²/t = 8.41e-3 m²/s
get_model_base_units()[source]

Get model base units in compact, parseable SI prefix form.

Returns a dictionary mapping dimension names to their compact SI unit representations. Uses Pint’s to_compact() to automatically select readable SI prefixes (e.g., ‘Mm’ for megameters, ‘Ps’ for petaseconds).

Returns:

Dictionary with dimension names as keys and compact SI unit strings as values

Return type:

dict

Examples

>>> model.set_reference_quantities(
...     mantle_depth=2900*uw.units.km,
...     plate_velocity=5*uw.units.cm/uw.units.year,
...     mantle_viscosity=1e21*uw.units.Pa*uw.units.s,
...     mantle_temperature=1500*uw.units.K
... )
>>> base_units = model.get_model_base_units()
>>> base_units['length']  # "2.9 megameter" (parseable)
>>> base_units['time']    # "1.83 petasecond" (parseable)
get_scale_summary()[source]

Get a human-readable summary of all fundamental scales.

Returns a formatted string showing the fundamental scales derived from reference quantities, including how each scale was derived and what reference quantities it makes close to unity.

Returns:

Multi-line string with formatted scale summary

Return type:

str

Examples

>>> model.set_reference_quantities(
...     mantle_depth=2900*uw.units.km,
...     plate_velocity=5*uw.units.cm/uw.units.year,
...     mantle_viscosity=1e21*uw.units.Pa*uw.units.s,
...     mantle_temperature=1500*uw.units.K
... )
>>> print(model.get_scale_summary())
Fundamental Scales Summary:
Length Scale: 2900 kilometer
  • From: mantle_depth

  • Makes: mantle_depth ≈ 1.0 in model units

Time Scale: 580 kilometer·year/centimeter
  • From: length_scale ÷ plate_velocity

  • Makes: plate_velocity ≈ 1.0 in model units

Mass Scale: 1.68e+27 kg (equivalent)
  • From: mantle_viscosity × length_scale × time_scale

  • Makes: mantle_viscosity ≈ 1.0 in model units

Temperature Scale: 1500 kelvin
  • From: mantle_temperature

  • Makes: mantle_temperature ≈ 1.0 in model units

list_derived_scales()[source]

List which scales were derived vs. directly specified.

Returns a dictionary categorizing fundamental scales by how they were obtained: either directly from reference quantities or derived from compound quantities.

Returns:

Dictionary with keys: - ‘direct’: List of (dimension, source) tuples for directly specified scales - ‘derived’: List of (dimension, derivation) tuples for derived scales - ‘missing’: List of standard dimensions that couldn’t be determined

Return type:

dict

Examples

>>> model.set_reference_quantities(
...     mantle_depth=2900*uw.units.km,
...     plate_velocity=5*uw.units.cm/uw.units.year,
...     mantle_viscosity=1e21*uw.units.Pa*uw.units.s,
...     mantle_temperature=1500*uw.units.K
... )
>>> derivation = model.list_derived_scales()
>>> derivation['direct']    # [('length', 'mantle_depth'), ('temperature', 'mantle_temperature')]
>>> derivation['derived']   # [('time', 'length_scale ÷ plate_velocity'), ('mass', 'mantle_viscosity × length_scale × time_scale')]
>>> derivation['missing']   # []
validate_dimensional_completeness(required_dimensions=None)[source]

Validate if reference quantities provide complete dimensional coverage.

Checks whether the current reference quantities span enough dimensions to derive fundamental scales for all required dimensions, and provides suggestions for completing under-determined systems.

Parameters:

required_dimensions (list, optional) – List of dimensions required (e.g., [‘length’, ‘time’, ‘mass’, ‘temperature’]). If None, uses the standard 4-dimension set.

Returns:

Validation result containing: - ‘status’: ‘complete’, ‘under_determined’, or ‘no_reference_quantities’ - ‘missing_dimensions’: List of missing dimensions (if any) - ‘suggestions’: List of suggested reference quantities to add - ‘derivable_dimensions’: List of dimensions that can be derived - ‘analysis’: Human-readable analysis string

Return type:

dict

Examples

>>> # Under-determined system
>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)
>>> result = model.validate_dimensional_completeness()
>>> result['status']  # 'under_determined'
>>> result['missing_dimensions']  # ['time', 'mass', 'temperature']
>>> # Complete system
>>> model.set_reference_quantities(
...     mantle_depth=2900*uw.units.km,
...     plate_velocity=5*uw.units.cm/uw.units.year,
...     mantle_viscosity=1e21*uw.units.Pa*uw.units.s,
...     mantle_temperature=1500*uw.units.K
... )
>>> result = model.validate_dimensional_completeness()
>>> result['status']  # 'complete'
set_scaling_mode(mode='exact')[source]

Set the scaling mode for fundamental scales.

Parameters:

mode ({'exact', 'readable'}) – Scaling mode to use: - ‘exact’: Reference quantities scale to exactly 1.0 (default) - ‘readable’: Reference quantities scale to O(1) with nice round numbers

Examples

>>> # Default exact mode: reference quantities become exactly 1.0
>>> model.set_scaling_mode('exact')
>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)
>>> model.to_model_units(2900*uw.units.km)  # → UWQuantity(1.0, 'model_length_units')
>>> # Readable mode: reference quantities become O(1) with nice scales
>>> model.set_scaling_mode('readable')
>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)
>>> model.to_model_units(2900*uw.units.km)  # → UWQuantity(2.9, 'model_length_units')
>>> # Internal scale becomes 1000 km instead of 2900 km
get_scaling_mode()[source]

Get the current scaling mode.

Returns:

Current scaling mode: ‘exact’ or ‘readable’

Return type:

str

show_optimal_units()[source]

Display the optimal units implied by this model’s reference quantities.

Shows fundamental scalings (length, time, mass, temperature) derived from reference quantities and suggests optimal units for various physical quantities.

scale_to_physical(coordinates, dimension='length')[source]

Convert dimensionless model coordinates to physical units.

This method converts model-unit coordinate arrays (where the model domain is scaled to ~1.0) back to physical units using the model’s fundamental scales.

Parameters:
  • coordinates (array-like) – Dimensionless coordinate array in model units

  • dimension (str, default 'length') – Fundamental dimension to use for scaling (‘length’, ‘time’, ‘mass’, ‘temperature’)

Returns:

Coordinates in physical units with appropriate units

Return type:

UWQuantity

Examples

>>> model.set_reference_quantities(domain_length=1000*uw.units.km, ...)
>>> mesh = uw.meshing.StructuredQuadBox(minCoords=(-3,-3), maxCoords=(3,3), ...)
>>> # mesh.points are in model units (dimensionless, domain spans -3 to 3)
>>> physical_coords = model.scale_to_physical(mesh.points)
>>> # Result: coordinates in kilometers, spanning -3000 to 3000 km
Raises:

ValueError – If no reference quantities set or dimension not available

to_model_units(quantity)[source]

Safely coerce any quantity to model units using smart protocol pattern.

This method is designed to be safe to call repeatedly and handles edge cases: 1. Does nothing if model has no units (no reference quantities set) 2. Does nothing if the quantity is already in model units 3. Does nothing if the quantity is dimensionless 4. Uses protocol pattern for extensible conversion

Parameters:

quantity (Any) – Quantity to convert (UWQuantity, Pint quantity, numeric, etc.)

Returns:

Converted quantity in model units, or original if no conversion needed

Return type:

UWQuantity or original quantity

has_units_active()[source]

Check if the units system is active (reference quantities have been set).

Returns:

True if reference quantities have been set, False otherwise.

Return type:

bool

to_model_magnitude(quantity, expected_dimension=None)[source]

Convert quantity to model units and extract numerical magnitude.

Deprecated since version This: method is deprecated. Use uw.scaling.non_dimensionalise() instead for consistent unit conversion across the codebase. The global scaling function ensures all conversions use the same COEFFICIENTS dictionary that is synchronized with model reference quantities.

This is a convenience method that combines to_model_units() with magnitude extraction for internal use. It handles the common pattern of converting user inputs (which may have units) to raw numerical values in model units for internal computations.

Parameters:
  • quantity (Any) – Quantity to convert (UWQuantity, Pint quantity, numeric, tuple, etc.)

  • expected_dimension (str, optional) – Expected dimension like ‘[length]’, ‘[time]’, etc. If provided and the quantity is a plain number when units are active, a warning is issued to alert the user that they may have forgotten units. Common values: ‘[length]’, ‘[time]’, ‘[temperature]’, ‘[mass]’

Returns:

Raw numerical magnitude in model units (dimensionless)

Return type:

float, int, or array-like

Examples

>>> model.set_reference_quantities(length_scale=1000*uw.units.km)
>>> coords_physical = [100*uw.units.km, 200*uw.units.km]
>>> coords_model = model.to_model_magnitude(coords_physical)
>>> coords_model
[0.1, 0.2]  # In model units (dimensionless)
>>> # Also works with plain numbers (pass-through)
>>> model.to_model_magnitude([0.1, 0.2])
[0.1, 0.2]
>>> # With expected_dimension, warns on plain numbers when units active
>>> model.to_model_magnitude(6370, expected_dimension='[length]')
UserWarning: Plain number 6370 passed for [length] parameter when units
are active. If you intended physical units, use uw.quantity(6370, 'km').
Value is being interpreted as 6370 model units.
>>> # Works with time
>>> dt_physical = 1000 * uw.units.year
>>> dt_model = model.to_model_magnitude(dt_physical)

Notes

This method is intended for internal use at API boundaries where user inputs need to be converted to model units for computations. The conversion respects dimensional analysis and reference quantities.

When expected_dimension is provided, this acts as a “gatekeeper” to catch potential bugs where users forget to attach units to their values.

from_model_magnitude(magnitude, dimensions)[source]

Convert numerical magnitude in model units back to physical quantity.

Deprecated since version This: method is deprecated. Use uw.scaling.dimensionalise() instead for consistent unit conversion across the codebase.

This is the inverse of to_model_magnitude() - it takes a raw numerical value in model units and wraps it with appropriate physical units based on the dimensional specification.

Parameters:
  • magnitude (float, int, or array-like) – Numerical value(s) in model units (dimensionless)

  • dimensions (str or Pint dimensionality) – Dimensional specification like ‘[length]’, ‘[time]’, ‘[length]/[time]’, etc. Can also be a Pint dimensionality object from quantity.dimensionality

Returns:

Physical quantity with appropriate units based on model reference scales

Return type:

UWQuantity

Examples

>>> model.set_reference_quantities(length_scale=1000*uw.units.km)
>>> coords_model = np.array([0.1, 0.2])  # Model coordinates
>>> coords_physical = model.from_model_magnitude(coords_model, '[length]')
>>> coords_physical
[100, 200] km  # Back to physical units
>>> # Works with composite dimensions
>>> velocity_model = 1.0  # Model velocity magnitude
>>> velocity_physical = model.from_model_magnitude(velocity_model, '[length]/[time]')
>>> # If no reference quantities, returns SI base units
>>> model_no_refs = uw.Model()
>>> result = model_no_refs.from_model_magnitude(100, '[length]')
>>> result
100 m  # SI base units when no scaling defined

Notes

This method is intended for converting internal model-unit values back to physical quantities when returning results to users. It ensures consistent unit handling across API boundaries.

to_dict()[source]

Export model configuration to dictionary for serialization.

Provides enhanced serialization with SymPy expression conversion and PETSc state capture for complete reproducibility.

Returns:

Model configuration dictionary suitable for JSON/YAML export

Return type:

dict

export_configuration()[source]

Alias for to_dict() for backward compatibility

Return type:

Dict[str, Any]

from_dict(config)[source]

Import model configuration from dictionary.

Handles enhanced serialization format with SymPy expression reconstruction. Note: Only imports materials and metadata for now. Mesh/variables/swarms must be recreated.

Parameters:

config (dict) – Configuration dictionary from to_dict() or YAML export

to_yaml(file_path=None)[source]

Export model configuration to YAML format.

Parameters:

file_path (str, optional) – If provided, write YAML to this file path

Returns:

YAML string representation of model configuration

Return type:

str

from_yaml(yaml_content=None, file_path=None)[source]

Import model configuration from YAML.

Parameters:
  • yaml_content (str, optional) – YAML string to parse

  • file_path (str, optional) – Path to YAML file to load

classmethod from_yaml_file(file_path)[source]

Create a new Model instance from YAML file.

Parameters:

file_path (str) – Path to YAML configuration file

Returns:

New model instance with loaded configuration

Return type:

Model

capture_petsc_state()[source]

Capture current PETSc options database state.

Returns all PETSc options currently set, enabling complete reproducibility of simulation parameters.

Returns:

Dictionary of PETSc option names and values

Return type:

dict

restore_petsc_state(petsc_options=None)[source]

Restore PETSc options database from captured state.

Parameters:

petsc_options (dict, optional) – PETSc options to restore. If None, uses self.petsc_state

set_petsc_option(option, value)[source]

Set a PETSc option and track it in model state.

Parameters:
  • option (str) – PETSc option name (without leading -)

  • value (str) – Option value

view()[source]

Display comprehensive model information following the established view() pattern.

Shows model configuration, units setup, registered components, and provides guidance for setting up units if not configured.

model_post_init(context, /)

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

Parameters:
  • self (BaseModel)

  • context (Any)

Return type:

None

Model Functions

underworld3.get_default_model()[source]

Get or create the default model for this UW3 session.

The default model automatically registers all meshes, swarms, variables, and solvers created during the session, enabling serialization and model orchestration without explicit user interaction.

Returns:

The default model instance for this session

Return type:

Model

Example

>>> import underworld3 as uw
>>> model = uw.get_default_model()
>>> print(model)  # See all registered objects
>>> config = model.to_dict()  # Serialize model
underworld3.reset_default_model()[source]

Reset the default model to a fresh instance.

Useful for testing or starting a new simulation in an interactive session. All previously registered objects will be orphaned from the model registry.

Also resets global state including strict units mode to default (ON).

Returns:

New default model instance

Return type:

Model

Example

>>> import underworld3 as uw
>>> uw.reset_default_model()  # Start fresh
underworld3.create_model(name=None)[source]

Create a new Model instance

Parameters:

name (str | None)

Return type:

Model

Thermal Convection Configuration

class underworld3.ThermalConvectionConfig[source]

Bases: BaseModel

Configuration model for thermal convection simulations.

Demonstrates how to create specialized parameter configurations that work with the enhanced Model infrastructure.

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

mesh_type: str
cellsize: float
qdegree: int
rayleigh_number: float
viscosity: float
thermal_diffusivity: float
temperature_top: float
temperature_bottom: float
velocity_boundary: str
stokes_solver_type: str
stokes_tolerance: float
advdiff_solver_type: str
dt: float
max_time: float
max_steps: int
to_petsc_options()[source]

Convert configuration to PETSc options dictionary.

Returns:

PETSc options suitable for setting via Model.set_petsc_option()

Return type:

dict

to_materials_dict()[source]

Convert configuration to materials dictionary format.

Returns:

Materials dictionary suitable for Model.materials

Return type:

dict

underworld3.create_thermal_convection_model(config, name='thermal_convection')[source]

Create a Model instance configured for thermal convection.

Demonstrates integration between specialized configs and Model infrastructure.

Parameters:
Returns:

Configured model ready for thermal convection simulation

Return type:

Model