Skip to content

DDt semi-Lagrangian: unit mismatch on geographic meshes with units active #112

Description

@lmoresi

Summary

The SemiLagrangian_DDt.update_pre_solve() in systems/ddt.py fails with unit incompatibility errors when used on geographic meshes with the units system active. The AdvDiffusion solver is unusable on geographic meshes.

Error

ValueError: Cannot subtract arrays with incompatible units: 'meter' and 'meter / second'

at ddt.py:1450:

mid_pt_coords = coords - v_at_node_pts * (0.5 * dt_for_calc)

Root Cause

Three quantities with inconsistent units:

  • coords = psi_star.coords → UnitAwareArray in meters (dimensional)
  • v_at_node_pts → dimensionalised to m/s (correct)
  • dt_for_calc → bare float or incorrectly handled (should be seconds)

When dt is passed as a bare float (nondimensional), line 1348-1349 treats it as dimensionless rather than converting to seconds:

# If dt is already a dimensionless number, treat it as seconds
dt_for_calc = dt  # BUG: doesn't actually convert to seconds

When dt is passed as a uw.quantity in seconds, the JIT compilation appears to hang (>30 min with no output) — likely due to the complex symbolic expressions from geographic basis vectors interacting with the dimensional dt.

Reproduction

import underworld3 as uw

uw.reset_default_model()
model = uw.Model()
model.set_reference_quantities(
    length=uw.quantity(1000, "km"),
    viscosity=uw.quantity(1e21, "Pa.s"),
    diffusivity=uw.quantity(1e-6, "m**2/s"),
)

mesh = uw.meshing.RegionalGeographicBox(
    lon_range=(135.5, 137.5), lat_range=(-34.5, -33.0),
    depth_range=(uw.quantity(0, "km"), uw.quantity(50, "km")),
    ellipsoid="WGS84", numElements=(8, 8, 4), simplex=True,
)

v = uw.discretisation.MeshVariable("v", mesh, mesh.dim, degree=1,
                                    continuous=True, units="m/s")
C = uw.discretisation.MeshVariable("C", mesh, 1, degree=2)

adv_diff = uw.systems.AdvDiffusion(mesh, u_Field=C, V_fn=v)
adv_diff.constitutive_model = uw.constitutive_models.DiffusionModel
adv_diff.constitutive_model.Parameters.diffusivity = 1e-4
adv_diff.add_essential_bc(1.0, "Bottom")

adv_diff.solve(timestep=1e-4)  # Fails with unit mismatch

Suggested Fix

The DDt code should either:

  1. Work in nondimensional DM space internally — strip units from coords, nondimensionalise velocity and dt, do all arithmetic in nondimensional, then re-wrap
  2. Consistently dimensionalise everything — ensure coords (m), velocity (m/s), and dt (s) are all in compatible SI units before arithmetic

Option 1 is simpler and matches how the PETSc solver backend works.

Additional Notes

  • Velocity variables must have units= set (e.g. units="m/s") for DDt to detect the dimensional mode. Without units, DDt gets V_fn units: dimensionless and the mismatch is different (dimensionless vs meters).
  • This also surfaces a broader issue: MeshVariable units should be required when the units system is active, not optional. Currently a missing units= causes silent failures in downstream solvers.

Context

Discovered while building the H2Ex fault-controlled flow workflow on the feature/fault-system-workflow branch. The Darcy velocity field needs to be advected through an AdvDiffusion solver for concentration field accumulation mapping.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions