@@ -428,13 +428,13 @@ The JIT compilation system needs to:
4284281 . Identify SymPy Function atoms in expressions
4294292 . Map them to PETSc vector components
4304303 . Generate C code with appropriate substitutions
431+ 4 . Allow constant parameters to change without recompilation
431432
432433## The Solution: Transparent SymPy Objects
433434
434435The mathematical object system preserves JIT compatibility by ensuring all operations return pure SymPy objects:
435436
436437``` python
437-
438438# User writes natural syntax
439439momentum = density * velocity
440440
@@ -447,23 +447,50 @@ atoms = momentum.atoms(sympy.Function) # Finds V_0, V_1
447447# Maps to velocity.fn for PETSc substitution
448448```
449449
450- ## Expression Unwrapping
450+ ## Expression Unwrapping and Constants
451451
452- The ` unwrap() ` function resolves nested expressions before compilation :
452+ The JIT compiler performs a ** two-phase unwrap ** on expressions :
453453
454- ``` python
454+ 1 . ** Phase 1 — Constants extraction** : ` UWexpression ` atoms that resolve to
455+ pure numbers (no spatial/field dependencies) are replaced with
456+ ` _JITConstant ` symbols that render as ` constants[i] ` in C code.
457+
458+ 2 . ** Phase 2 — Full unwrap** : Remaining ` UWexpression ` atoms are expanded
459+ to their numerical values and baked into the C code.
455460
461+ ``` python
456462# Expression with nested UWexpressions
457463complex_expr = alpha * (temperature - T0) * velocity
458464
459- # unwrap() substitutes all UWexpression.sym values
460- unwrapped = unwrap(complex_expr)
461- # Result: 2e-5 * (T(x,y,z) - 293) * Matrix([[V_0(x,y,z)], [V_1(x,y,z)]])
465+ # Phase 1: alpha and T0 are constants → constants[0], constants[1]
466+ # Phase 2: temperature and velocity are field variables → petsc_a[], petsc_u[]
467+
468+ # Generated C code:
469+ # result = constants[0] * (petsc_a[0] - constants[1]) * petsc_u[0];
470+ ```
471+
472+ This means ** changing ` alpha ` or ` T0 ` between solves does not require
473+ recompilation** — only ` PetscDSSetConstants() ` is called (~ 0ms vs ~ 10s).
474+
475+ ## Why Use UWexpressions for Solver Parameters
476+
477+ ** UWexpressions are the preferred way to define solver parameters.**
478+ Using raw numbers forces recompilation when values change:
462479
463- # JIT compilation proceeds normally
464- compiled = uw.systems.compile(unwrapped)
480+ ``` python
481+ # PREFERRED — expression parameter, efficient for time-stepping
482+ eta = uw.expression(" eta" , 1e21 )
483+ stokes.constitutive_model.Parameters.shear_viscosity_0 = eta
484+ # Changing eta.sym later → no recompilation
485+
486+ # AVOID for time-varying parameters — raw number, requires rebuild
487+ stokes.constitutive_model.Parameters.shear_viscosity_0 = 1e21
488+ # Changing this later → full JIT rebuild (~10s)
465489```
466490
491+ This is particularly important for viscoelastic and Navier-Stokes
492+ solvers where parameters like ` dt_elastic ` change every time step.
493+
467494# Migration from Legacy Patterns
468495
469496## Automatic Migration Patterns
0 commit comments