Overview¶
uw.Params provides a clean way to define configurable parameters that can be:
Set in notebooks - Just assign new values
Overridden from command line - Via PETSc options (
-uw_param_name value)
This makes scripts portable between interactive development and HPC batch execution.
Basic Usage¶
The recommended pattern is to define default values as named constants before
the uw.Params block. This separates “what are the defaults” (easy to find and
edit in a notebook) from “how are they validated and overridden” (the uw.Params
machinery).
import underworld3 as uw
# --- Default values (edit these in a notebook) ---
RESOLUTION = 0.05 # cell size for mesh
DIFFUSIVITY = 1.0 # material property
MAX_STEPS = 100 # solver iterations
params = uw.Params(
uw_resolution = RESOLUTION,
uw_diffusivity = DIFFUSIVITY,
uw_max_steps = MAX_STEPS,
uw_verbose = True, # Boolean flag
uw_solver = "mumps", # String option
)
# Use in your model
mesh = uw.meshing.Box(cellSize=params.uw_resolution)
Naming Convention¶
Use the uw_ prefix for parameter names. This:
Avoids collisions with PETSc solver options
Makes it clear these are underworld example parameters
The CLI flag matches the Python name exactly
Command Line Override¶
# Override resolution and solver
python script.py -uw_resolution 0.025 -uw_solver superlu_dist
# Works with mpirun
mpirun -np 4 python script.py -uw_resolution 0.01
Why Single-Dash Options?¶
Underworld uses PETSc-style command-line options, not Python’s standard argparse
conventions. The key differences:
Convention |
Long option |
Short option |
Word separator |
|---|---|---|---|
PETSc (Underworld) |
|
— |
underscore |
argparse (Python) |
|
|
hyphen |
Underworld inherits PETSc’s options database, which uses a single dash followed by a descriptive name with underscores. This is by design:
PETSc solver options (e.g.,
-ksp_type gmres,-pc_type lu) use this formatUnderworld user parameters share the same options database
The
uw_prefix prevents collisions with PETSc’s own options
This means standard Python option parsers (argparse, click) do not support this
style of options by default and may require custom handling if you try to parse
-uw_* flags yourself. Use uw.Params instead — it reads from PETSc’s options
database automatically.
Tip
If you’re writing a wrapper script that also needs argparse-style options,
parse those with argparse first (for example using parse_known_args),
then pass only the remaining/unparsed arguments to PETSc via
petsc4py.init(remaining_argv) before importing underworld.
Notebook Override¶
# Just assign a new value
params.uw_resolution = 0.025
Unit-Aware Parameters¶
For physical quantities with units, use the uw.Param() wrapper:
params = uw.Params(
# Physical quantities with units
uw_cell_size = uw.Param(0.5, units="km", description="Mesh cell size"),
uw_viscosity = uw.Param(1e21, units="Pa*s"),
uw_latitude = uw.Param(45.0, units="degree"),
# Dimensionless ratios
uw_rayleigh = uw.Param(1e5, type=uw.ParamType.RATIO,
description="Rayleigh number"),
# Plain types still work
uw_elements = 32,
)
CLI with Units¶
Units must be provided on the command line for unit-aware parameters:
# These work
python script.py -uw_cell_size "500 m"
python script.py -uw_cell_size 0.5km
python script.py -uw_viscosity "1e22 Pa*s"
python script.py -uw_latitude "0.785 radian" # Converts to degrees
# These fail with helpful errors
python script.py -uw_cell_size 500 # ERROR: Units required
python script.py -uw_cell_size "500 s" # ERROR: Dimension mismatch
Accessing Values¶
Unit-aware parameters return UWQuantity objects that integrate with the scaling system:
cell_size = params.uw_cell_size # UWQuantity(0.5, "km")
cell_size_m = cell_size.to("meter") # Convert to meters
# For mesh creation (handles unit conversion automatically)
mesh = uw.meshing.Box(cellSize=params.uw_cell_size)
Parameter Options¶
The uw.Param() wrapper supports:
Option |
Description |
|---|---|
|
Default value (required) |
|
Unit string, e.g., |
|
Explicit type: |
|
Tuple |
|
Help text shown in |
Bounds Validation¶
params = uw.Params(
uw_cell_size = uw.Param(0.5, units="km",
bounds=(0.01, 100),
description="Must be 0.01-100 km"),
)
# This would raise ValueError
# python script.py -uw_cell_size "0.001 km" # Below minimum
Inspecting Parameters¶
Display in Notebooks¶
Params has rich display with source tracking:
params # Shows table with values, units, types, and sources
Parameter |
Value |
Units |
Type |
Source |
|---|---|---|---|---|
|
|
km |
quantity |
default |
|
|
Pa*s |
quantity |
CLI |
CLI Help¶
print(params.cli_help())
Command-line options (PETSc format):
-uw_cell_size <quantity> Units: km
Must be 0.01-100 km
(default: 0.5 km)
-uw_viscosity <quantity> Units: Pa*s
(default: 1e+21 Pa*s)
-uw_rayleigh <ratio> Dimensionless ratio
Rayleigh number
(default: 100000.0)
Example:
python script.py -uw_cell_size 0.5km -uw_viscosity 1e+21Pa*s
Complete Example¶
import underworld3 as uw
# --- Default values (edit these in a notebook) ---
CELL_SIZE = 50.0 # km – target cell size
DEPTH = 660.0 # km – model depth
VISCOSITY = 1e21 # Pa·s – reference viscosity
DENSITY_DIFF = 50.0 # kg/m³ – density contrast
MAX_ITERATIONS = 50
TOLERANCE = 1e-6
# Define all configurable parameters at the top
params = uw.Params(
# Mesh parameters
uw_cell_size = uw.Param(CELL_SIZE, units="km",
bounds=(10, 200),
description="Target cell size"),
uw_depth = uw.Param(DEPTH, units="km",
description="Model depth"),
# Physical properties
uw_viscosity = uw.Param(VISCOSITY, units="Pa*s"),
uw_density_diff = uw.Param(DENSITY_DIFF, units="kg/m^3"),
# Solver settings
uw_max_iterations = MAX_ITERATIONS,
uw_tolerance = TOLERANCE,
)
# Show help (useful at script start)
if uw.mpi.rank == 0:
print(params.cli_help())
# Build model using parameters
mesh = uw.meshing.Box(
minCoords=(0, 0),
maxCoords=(uw.quantity(2000, "km"), params.uw_depth),
cellSize=params.uw_cell_size,
)
# ... rest of model setup
Run with:
# Default parameters
python convection.py
# Override for higher resolution
python convection.py -uw_cell_size 25km -uw_depth 1000km
# HPC run with many overrides
mpirun -np 256 python convection.py \
-uw_cell_size 10km \
-uw_viscosity "5e20 Pa*s" \
-uw_max_iterations 100
Using Parameters with Solvers: Expressions¶
When passing parameters to solver constitutive models, wrap them in
uw.expression(). This creates a named symbolic container that the JIT
compiler can update efficiently — changing the value between time steps
does not trigger recompilation of the C extension.
import underworld3 as uw
# Define parameters
VISCOSITY = 1e21 # Named constant
MODULUS = 1e10 # Named constant
DT = 0.01 # Timestep
params = uw.Params(
uw_viscosity = VISCOSITY,
uw_modulus = MODULUS,
)
# Wrap in expressions for solver use
eta = uw.expression("eta", params.uw_viscosity)
mu = uw.expression("mu", params.uw_modulus)
dt_e = uw.expression("dt_e", DT)
# Pass expressions to constitutive model
stokes.constitutive_model.Parameters.shear_viscosity_0 = eta
stokes.constitutive_model.Parameters.shear_modulus = mu
stokes.constitutive_model.Parameters.dt_elastic = dt_e
# Time-stepping: change dt without recompilation
for step in range(100):
dt_e.sym = compute_new_timestep() # Updates value, ~0ms
stokes.solve() # No JIT rebuild needed
Without expressions, changing a solver parameter between steps requires
setting _force_setup=True on the solve call, which triggers a full JIT
recompilation (~5–15 seconds). With expressions, parameter updates go
through PETSc’s constants[] array and cost essentially nothing.
Angle Units¶
Angles work naturally - you can define in degrees and provide radians (or vice versa):
params = uw.Params(
uw_latitude = uw.Param(45.0, units="degree"),
)
# CLI accepts any angle unit
# python script.py -uw_latitude "0.785 radian"
# python script.py -uw_latitude "45 deg"
# Convert as needed
lat_rad = params.uw_latitude.to("radian")