diff --git a/.claude/skills/adapt-on-top-faults/SKILL.md b/.claude/skills/adapt-on-top-faults/SKILL.md index a209d5b14..baaf9285a 100644 --- a/.claude/skills/adapt-on-top-faults/SKILL.md +++ b/.claude/skills/adapt-on-top-faults/SKILL.md @@ -119,8 +119,8 @@ constraint in a per-node (n,t) frame → machine-zero leakage AND composes with ```python nhat = mesh.CoordinateSystem.unit_e_0 # exact radial normal (annulus/sphere) -stokes.add_rotated_freeslip_bc("Upper", normal=nhat) -stokes.add_rotated_freeslip_bc("Lower", normal=nhat) +stokes.add_rotated_freeslip_bc(0, "Upper", normal=nhat) +stokes.add_rotated_freeslip_bc(0, "Lower", normal=nhat) stokes.petsc_use_pressure_nullspace = True # enclosed -> pressure gauge stokes.solve() # rigid-rotation gauge auto-removed # Convergence status: read stokes._rotated_freeslip_info = {ksp_reason, diff --git a/.claude/skills/free-surface-convection/SKILL.md b/.claude/skills/free-surface-convection/SKILL.md index e6687ef0e..d8228640a 100644 --- a/.claude/skills/free-surface-convection/SKILL.md +++ b/.claude/skills/free-surface-convection/SKILL.md @@ -32,7 +32,7 @@ Two Stokes solves per step on the SAME mesh, then a pointwise surface update: pinned by the stress-free condition → no pressure nullspace). The surface normal velocity `u_n` of this solve IS the kinematic rate `ḣ`. 2. **Held-lid solve** — a second Stokes solve with a RIGID free-slip held lid - (`u_n = 0`, via `add_nitsche_bc(Upper, local_h=True)` — see [[project_nitsche_local_h_pr275]]) + (`u_n = 0`, via `add_nitsche_bc(0.0, "Upper", local_h=True)` — see [[project_nitsche_local_h_pr275]]) and a DRIVING-ONLY body force. Its surface normal stress `σ_nn` gives the equilibrium topography `h_∞ = -(σ_nn - mean)/ρg`. (The free solve forces `σ_nn = 0`, so the equilibrium MUST come from the held-lid stress.) diff --git a/CLAUDE.md b/CLAUDE.md index 6dbf6522b..0f4a12721 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -326,7 +326,7 @@ The PETSc-based solvers are carefully optimized and validated. **NO CHANGES with ## Boundary Conditions: Free-slip -**Prefer rotated strong free-slip** (`solver.add_rotated_freeslip_bc(boundary, normal=None)`) +**Prefer rotated strong free-slip** (`solver.add_rotated_freeslip_bc(conds, boundary, normal=None)`, value-first: `conds=0` is the only implemented datum) to impose `v·n̂ = 0`: - Enforces zero wall-normal flow to **machine precision** (Nitsche / penalty leak ~1e-3). @@ -351,7 +351,9 @@ strong `v_n=0`, reaction = σ_nn); the solve dispatch is in ## Data Access Patterns -**Authoritative Reference**: `docs/developer/UW3_Style_and_Patterns_Guide.md` +**Authoritative Reference**: `docs/developer/subsystems/data-access.md` +(governing document per the Style Charter §10 authority map; see also the +master authority index in `docs/developer/index.md`) **Pattern Checker**: Use `/check-patterns` to scan for deprecated patterns ### Quick Summary @@ -361,7 +363,9 @@ strong `v_n=0`, reaction = σ_nn); the solve dispatch is in | `with swarm.access(var):` | **Deprecated** | Direct: `var.data[...]` | | `mesh.data` (coordinates) | **Deprecated** | `mesh.X.coords` | -See `docs/developer/UW3_Style_and_Patterns_Guide.md` and `docs/developer/subsystems/data-access.md` for full patterns, array shapes, and cache safety details. +See `docs/developer/subsystems/data-access.md` for full patterns, array shapes, +and cache safety details (`docs/developer/UW3_Style_and_Patterns_Guide.md` is the +broader style reference; where the two disagree, `data-access.md` governs). --- diff --git a/docs/advanced/curved-boundary-conditions.md b/docs/advanced/curved-boundary-conditions.md index 9e36615eb..7201d5ef4 100644 --- a/docs/advanced/curved-boundary-conditions.md +++ b/docs/advanced/curved-boundary-conditions.md @@ -41,7 +41,7 @@ Nitsche's method provides a variationally consistent alternative to penalty that is insensitive to the penalty magnitude and gives optimal convergence: ```python -stokes.add_nitsche_bc("Upper", gamma=10) +stokes.add_nitsche_bc(0.0, "Upper", gamma=10) ``` The method automatically constructs penalty, consistency (stress flux), @@ -51,13 +51,13 @@ resolution or viscosity. **Prescribed normal velocity:** ```python -stokes.add_nitsche_bc("Inlet", g=1.0, gamma=10) +stokes.add_nitsche_bc(1.0, "Inlet", gamma=10) ``` **Custom constraint direction** (e.g., fault normal different from surface normal): ```python fault_normal = sympy.Matrix([0.6, 0.8]) -stokes.add_nitsche_bc("Fault", direction=fault_normal, gamma=10) +stokes.add_nitsche_bc(0.0, "Fault", direction=fault_normal, gamma=10) ``` **When to use:** @@ -300,7 +300,7 @@ in 3D spherical models where penalty sensitivity becomes problematic. ## Tips for Success -1. **Start with Nitsche**: `stokes.add_nitsche_bc("Upper", gamma=10)` — no penalty tuning needed +1. **Start with Nitsche**: `stokes.add_nitsche_bc(0.0, "Upper", gamma=10)` — no penalty tuning needed 2. **For penalty BCs, always normalize**: Analytical formulas need explicit normalization 3. **Check orientation**: Ensure normals point outward (use `sign(r.dot(normal))`) 4. **Verify visually**: Plot the normal field to catch errors diff --git a/docs/developer/CHANGELOG.md b/docs/developer/CHANGELOG.md index 0d068fd1f..fe933594c 100644 --- a/docs/developer/CHANGELOG.md +++ b/docs/developer/CHANGELOG.md @@ -4,8 +4,205 @@ This log tracks significant development work at a conceptual level, suitable for --- +## 2026 Q3 (July – September) + +### July 2026 Quality Campaign — Audit, Style Charter, Remediation Waves (July 2026) + +**A systematic post-development-burst quality campaign**: six adversarially +verified review dimensions (loose ends, API consistency, readability, swarm +subsystem, docs coherence, branch triage) produced a ranked remediation +worklist, and the first waves have landed (#317, #322, #325, #326, #309–#312, +#334). + +- **UW3 Style Charter** adopted as the normative coding contract for every + development session, human or AI (`docs/developer/UW3_STYLE_CHARTER.md`), + with the campaign's review documents under `docs/reviews/2026-07/` (#317). + The maintainer's 18 decision rulings are recorded in the worklist (#322). +- **Track-0 bug fixes**: RBF derivative path now uses the requested component + rather than component 0 (#312); `CellWiseIntegral` evaluates on the mesh DS + instead of a mis-cloned P1 DM (#311); Lagrangian history writes use the + modern data layout (#310); honest 2D-only guard on the MMPDE mover (#309); + and a medium-severity batch — parallel `BoxInternalBoundary`, SL theta + restore, viewer crash, projection double-count, units-boundary honesty (#326). +- **Wave A deletions**: the `persistence.py` stub module removed + (behaviour-neutral), the unsupported coordinate-units feature family pruned + with the mesh `units=` kwarg deprecated, and the design-directory experiment + artifacts re-filed under `design/experiments/` (#325). +- **Wave C API harmonization**: the newer BC methods (`add_nitsche_bc`, + `add_rotated_freeslip_bc`, `add_constraint_bc`) migrated to the canonical + value-first signature `(conds, boundary, ...)` with one-warning deprecation + shims for the legacy boundary-first / `g=` spellings; `conds` is the single + BC datum name across all BC methods (#334). + +### Rotated Strong Free-Slip, Boundary Traction and Dynamic Topography (July 2026) + +**New `solver.add_rotated_freeslip_bc(...)`** imposes free-slip +(v·n̂ = 0) to machine precision by rotating boundary velocity DOFs into a +per-node (normal, tangential) frame and constraining the rotated normal +component exactly — correct on curved, tilted, and deformed boundaries (#293). + +- The constraint **reaction** is the consistent boundary normal traction + σ_nn: `boundary_normal_traction()` / `dynamic_topography()` recover surface + topography with no augmented-Lagrangian splitting (#293), and the rotated + path now runs **inside the nonlinear SNES**, with a numerical nonlinearity + probe that fails fast instead of silently returning a single linearisation + for a nonlinear rheology (#298). +- Schur-preconditioning parity: native 1/μ-mass USER Schur preconditioner, + 3D rotation nullspace, and an SVD-robust FMG coarse solve took the Zhong + spherical benchmark from 44 outer iterations to 1 (#306). +- A general **consistent boundary flux (CBF) primitive** recovers boundary + fluxes for any solver — surface heat flux / Nusselt number for scalar + diffusion, boundary traction σ·n for Stokes (#294). +- Recorded as the preferred free-slip BC in the project guidance (#300); + conda PETSc floor raised to ≥ 3.25 for FMG/rotation API consistency (#304). + +### Generalized Geometric Multigrid via Custom Prolongation (July 2026) + +**Geometric multigrid decoupled from the nested `refine()` hierarchy**: custom +(barycentric or RBF) prolongation operators drive PCMG across independent — +possibly non-nested — coarse/fine mesh pairs, with coarse operators assembled +by Galerkin RAP (#290). + +- Ties native FMG iteration-for-iteration on nested hierarchies while + supporting graded and adapted meshes that native FMG cannot nest. +- Native geometric FMG is locked out for single-field solvers where its DM + assumptions are invalid (#297). + +### Consistent Jacobian Tangent — Opt-In Newton (July 2026) + +**New opt-in `solver.consistent_jacobian`** (default `False` keeps the +bit-identical Picard tangent) fixes the unwrap-before-differentiate order so +the assembled Jacobian sees the strain-rate dependence of nonlinear +viscosities, plus a `"continuation"` mode that stages Picard → Newton for +robustness far from the solution (#258). + +### Swarm Correctness: Stale Caches and Parallel Checkpoint Restore (July 2026) + +**Swarm data-pipeline hardening across serial and parallel paths.** + +- Three stale-cache bugs after swarm particle addition fixed (#216), followed + by the campaign's Track-0 batch: cache invalidation ahead of the migrate() + early return, migration-suppression semantics, empty-rank KDTree guards, + and a pre-solve proxy refresh (#313). +- Parallel `read_timestep` restores each particle exactly once via a + rank-0-routed read (#329); reduction return types aligned to the + MeshVariable per-component contract, `NodalPointSwarm` deprecated, and the + never-functional `recycle_rate > 1` machinery excised (#323). + +### NumPy 2 and Environment Support (July 2026) + +**NumPy ≥ 2.0 supported** (#301), with a follow-up fix for 2-D `np.cross` on +`UnitAwareArray` under numpy 2 (#305). The repository now ships its Claude +Code skills for AI-assisted development sessions (#299). `UWQuantity` handles +offset temperature units (degC/degF) correctly (#295), and boundary rebuilds +avoid a PETSc IS size query that could abort on empty strata (#288). + +--- + ## 2026 Q2 (April – June) +### Mesh Adaptation: Metric-Driven Movers and MMPDE Robustness (May – June 2026) + +**A family of metric-driven mesh-adaptation movers** landed and hardened: +`smooth_mesh_interior` (Winslow Jacobi smoother, #190), `follow_metric` with +optimal-transport movers and `mesh.OT_adapt()` (#209), and anisotropic movers +with mesh-owned boundary tangent-slip and MPI robustness (#228). + +- Parallel adaptive "seam-spike" fixed: mover heap corruption, point-locator + hardening, and a redesigned remesh field transfer (#213). +- MMPDE metric hardening: SPD floor stops silent NaN-bail on deformed meshes + (#259); monotone RBF metric bake from nodal values (#266). +- Deformed-mesh correctness: boundary normals and domain-membership tests + track mesh deformation (#264); `on_boundary` acceptance for on-face point + queries (#207); gmsh `spacedim` no longer leaks across imports (#238). + +### Moving-Mesh Field Transfer and deform() (June 2026) + +**Mesh coordinate mutation made foolproof**: a capability gate plus the +public `mesh.deform()` entry point, with semi-Lagrangian CARRY field transfer +across deformation (#246, locked by regression test in #249). + +- Old-frame semi-Lagrangian reach-back for moving meshes (SLCN / SL-BDF2) + traces advected histories in the pre-deformation frame (#251). +- Evaluate / DMInterp / topology caches are invalidated on mesh deformation + (#188). + +### Semi-Lagrangian Accuracy and Timestep Controls (May 2026) + +**Fixed the semi-Lagrangian trace-back FE overshoot and added a monotone +limiter** to the DDt schemes (#186), exposed as `monotone_mode` on +`AdvDiffusionSLCN` (#189) and promoted to a universal evaluator flag (#208). + +- `theta` exposed on the SemiLagrangian DDt for backward-Euler / + Crank-Nicolson selection (#187). +- Tensor evaluate path in `_project_to_work_variable` (#185); NavierStokes + SLCN projection shape mismatch fixed (#183); vector DMInterpolation + overshoot at cell-shared boundaries fixed (#164). +- `estimate_dt` gained opt-in median/percentile cell reduction for + sliver-robust timesteps (#220). + +### Snapshot and Checkpoint Toolkit (May 2026) + +**A snapshot toolkit — "git stash for timesteps"**: in-memory snapshots +(#195), `Model.tracker` for snapshot-managed run state (#196), and an on-disk +snapshot format v1.1 with a metadata wrapper around PETSc bulk data (#198, +docs in #199). PETSc DMPlex checkpoint reload for mesh variables landed as +the underlying primitive (#146, T. Gollapalli). + +### Stokes_Constrained: Multiplier Free-Slip and Parallel Correctness (June 2026) + +**In-saddle Lagrange-multiplier free-slip with surface topography recovery** +in `Stokes_Constrained` (#224), then made parallel-correct. + +- `selfp` Schur preconditioner default, viscosity-scaled penalty, and + nullspace re-setup fix (#229); over-conservative serial guard removed + (#240); gauge, convergence, knockout, and rotation-gauge fixes (#265). + +### Boundary Conditions: Local-h Nitsche and Boundary-Slip Surfaces (June 2026) + +**Nitsche penalty scaled by local per-cell mesh size** rather than the global +minimum radius, restoring correct stiffness on graded and adapted meshes +(#275). + +- `mesh.boundary_slip` API with `BoundingSurface` objects for boundary + tangent-slip (#225); `Surface.influence_function` respects finite edges + (#241). + +### Units System: Quantity Interoperability and the ND Boundary Contract (June 2026) + +**UWQuantity operands now work across the API surface**: MeshVariable +arithmetic (#283), the Stokes bodyforce setter (#284), and units-active +semi-Lagrangian trace-back (#277). + +- The non-dimensional ↔ units boundary contract is documented as a design + contract (#278). +- Tutorials and examples repaired for strict units: thermal convection + (#263), dimensionality demo (#261), unit-aware coordinate evaluation (#262). + +### Memory, Evaluation and Solver Infrastructure (May – June 2026) + +**Comprehensive memory-leak fixes** in solver setup, interpolation caching, +and SubDM synchronisation (#178), Cython deallocation and callback hardening +(#181), and a `memprobe` diagnostic module (#179); cached spatial indexing +(KDTree) consolidated (#182). + +- `global_evaluate`: faithful parallel `evaluate()` fixing out-of-domain + mislocation (#222); swarm particle loss across rank boundaries during + advection fixed (#177); empty-partition reshape crash in parallel + `read_timestep` fixed (#221). +- Manifold-mesh PDE support with `Mesh.extract_surface` for solving on + embedded surfaces (#237). +- Exponential time-differencing VE/VEP integrators, ETD-1 default (#161); + per-iteration SNES update callbacks with pressure gauge and + boundary-correct scatter (#250); SolCx analytic solution ported as + `uw.function.analytic.SolCx` with its exact stress tensor (#223, #226); + projection gained unit-aware `smoothing_length` (#234) and an opt-in + `linear_solver()` (#281). +- XDMF output moved to PETSc-native topology with explicit cell-to-vertex + connectivity for ParaView (#218, #205); Gadi Singularity container build + files (#133); maturity-gated release tooling `./uw dev` (#233); `-uw_*` + CLI overrides applied on all platforms (#280). + ### DDt.set_initial_history — Public API for BDF Restart (April 2026) **New `set_initial_history(values, dt=...)` method on `SemiLagrangian` and diff --git a/docs/developer/UW3_Style_and_Patterns_Guide.md b/docs/developer/UW3_Style_and_Patterns_Guide.md index 1b76f6ce4..97244ffca 100644 --- a/docs/developer/UW3_Style_and_Patterns_Guide.md +++ b/docs/developer/UW3_Style_and_Patterns_Guide.md @@ -1,27 +1,12 @@ --- title: "Underworld3 Style and Patterns Guide" subtitle: "Development Standards and Architectural Patterns" -author: "Underworld Development Team" -date: today -execute: - enabled: false -format: - html: - toc: true - toc-depth: 3 - toc-location: left - number-sections: true - code-fold: true - theme: cosmo - pdf: - documentclass: report - geometry: margin=1in - toc: true - number-sections: true --- ```{note} Document Purpose -This guide documents the established patterns, conventions, and architectural decisions for Underworld3 development. It serves as a reference for maintaining consistency across the codebase. +This guide documents the established patterns, conventions, and architectural decisions for Underworld3 development. It serves as a detailed reference for maintaining consistency across the codebase. + +The normative style contract is the [UW3 Style Charter](UW3_STYLE_CHARTER.md) — where this guide and the Charter disagree, **the Charter wins**. See the authority map in [the developer documentation index](index.md). ``` # Code Organization @@ -67,22 +52,22 @@ Properties should return array-like objects that can trigger updates when modifi ```python -class Mesh: +class Field: @property def data(self): - """Mesh coordinate data with reactive callbacks.""" + """Field data with reactive callbacks.""" if self._cached_data is None: self._cached_data = NDArray_With_Callback( - self._coordinates, + self._values, owner=self ) - self._cached_data.set_callback(self._on_coordinates_changed) + self._cached_data.set_callback(self._on_values_changed) return self._cached_data - - def _on_coordinates_changed(self, array, change_info): - # Invalidate cached computations - self._jacobians = None - self._mesh_quality = None + + def _on_values_changed(self, array, change_info): + # Invalidate cached computations that depend on the values + self._interpolant = None + self._stats_cache = None ``` ## Property with Getter/Setter Pattern @@ -104,57 +89,60 @@ When properties need to behave like arrays but with additional functionality: ```python -# Users should access: mesh.data[...] instead of mesh.data +# Users index into the property: var.data[...] rather than rebinding var.data # Properties return NDArray_With_Callback for transparent numpy compatibility ``` # Documentation Style -## Markdown Docstrings for pdoc/pdoc3 +## NumPy/Sphinx Docstrings -Use markdown format with mathematics support: +Docstrings use **NumPy style with RST markup** (`:math:`, ``double backticks`` +for code). This is the settled standard (Style Charter §6); the docstrings turn +into the Sphinx documentation and render well in Jupyter. The conversion of older +docstrings is tracked in `docs/plans/docstring-conversion-plan.md`. ```python -class MyClass: - """ - # MyClass - - Brief description with **bold** and *italic* formatting. - - ## Mathematical Representation - - Given an array $\\mathbf{A} \\in \\mathbb{R}^{n \\times m}$, operations follow: - - $$\\mathbf{A}' = \\mathcal{O}(\\mathbf{A}) \\implies \\text{callback}(\\mathbf{A}', \\text{info})$$ - - ## Usage Examples - - ### Basic Usage - ```python - obj = MyClass([1, 2, 3]) - obj.set_callback(my_callback) - ``` - - ## Advanced Features - - - **Feature 1**: Description - - **Feature 2**: Description - - ## Performance Notes - - - **Zero overhead** when disabled - - **Minimal impact** during normal operations +def solve_diffusion(kappa, delta_t, monotone=False): + r"""Advance the diffusion equation by one timestep. + + Integrates :math:`\partial_t T = \nabla \cdot (\kappa \nabla T)` with an + implicit theta-scheme. + + Parameters + ---------- + kappa : float or sympy expression + Diffusivity :math:`\kappa`. May depend on position or on other + mesh variables. + delta_t : float + Timestep. Non-dimensionalised internally when the model carries units. + monotone : bool, default=False + Clamp interpolation overshoot during the semi-Lagrangian trace-back. + + Returns + ------- + MeshVariable + The updated temperature field. + + Examples + -------- + >>> T_new = solve_diffusion(1.0, 0.01) + + Notes + ----- + We do not shy away from equations in docstrings: state the weak form or the + scheme where it aids the reader. """ ``` ## Key Documentation Elements -- Use `#` headers for structure -- Include mathematical notation with LaTeX +- NumPy sections in the standard order: summary line, extended description, + ``Parameters``, ``Returns``, ``Examples``, ``Notes``, ``See Also`` +- Mathematical notation via RST ``:math:`` roles (raw strings ``r"""`` for backslashes) - Provide complete, runnable examples -- Use tables for parameter documentation -- Include performance considerations +- Include performance considerations where they matter (in ``Notes``) # Array and Data Management @@ -200,25 +188,22 @@ vector.array[:, 0, :] = all_components # Full vector ```python # Preferred: Direct array access with proper indexing -temperature.array[:, 0, 0] = temp_values # Scalar -velocity.array[:, 0, :] = vel_field # Vector -mesh.data[0] = new_position # Mesh coordinates -swarm.data += displacement # Swarm positions +temperature.array[:, 0, 0] = temp_values # Scalar +velocity.array[:, 0, :] = vel_field # Vector + +# Mesh coordinates: read via mesh.X.coords; deform via mesh.deform() +coords = mesh.X.coords # (N, dim) read access +mesh.deform(mesh.X.coords + displacement) # coordinate changes go through deform() + +# Swarm particle positions: the public coords property (getter and setter) +swarm.coords = swarm.coords + displacement # Avoid: Incorrect indexing # scalar.array[:, 0] = values # Missing third index! # vector.array[:, i] = values # Missing middle index! -``` -## Coordinate System Transformations - -```python - -# Reference changes throughout codebase -# OLD: swarm.particle_coordinates -# NEW: swarm._particle_coordinates -# OLD: mesh.deform_mesh -# NEW: mesh._deform_mesh +# Avoid: deprecated coordinate accessors (kept only so old code runs) +# mesh.data, mesh.points, swarm.data, swarm.points ``` # Context Managers @@ -265,11 +250,12 @@ with arr.delay_callback("batch update"): arr[2] = 3 # All callbacks fire here with MPI barriers -# Global coordination -with NDArray_With_Callback.delay_callbacks_global("mesh deformation"): - mesh.data += displacement - swarm.data += velocity * dt +# Global coordination across several variables +with NDArray_With_Callback.delay_callbacks_global("field update"): + temperature.array[:, 0, 0] = temp_values + velocity.array[:, 0, :] = vel_values # Synchronized execution across all arrays +# (uw.synchronised_array_update() is the public spelling of this context) ``` ## Custom Context Managers @@ -419,8 +405,11 @@ def test_callback_triggering(): ## Documentation Files - **Developer docs**: `underworld3/docs/developer/` -- **Format**: Quarto markdown (`.qmd`) -- **Naming**: Descriptive names with purpose (e.g., `UW3_Developers_NDArrays.qmd`) +- **Format**: MyST Markdown (`.md`), built with Sphinx — see the + "Documentation Requests" section of `CLAUDE.md` for admonition/math syntax +- **Naming**: Descriptive names with purpose (e.g., `UW3_Developers_NDArrays.md`) +- **Integration**: add new documents to the appropriate `toctree` in the parent + `index.md` and verify with `pixi run docs-build` ## Test Files @@ -456,7 +445,7 @@ def test_callback_triggering(): 1. **Reactive Properties**: Return NDArray_With_Callback with owner and callbacks 2. **Context Managers**: Use for state management and batch operations 3. **MPI Integration**: Always include barriers with error handling -4. **Documentation**: Markdown with mathematics for pdoc/jupyter compatibility +4. **Documentation**: NumPy/Sphinx docstrings with RST `:math:`; MyST `.md` docs 5. **Testing**: Comprehensive callback and functionality testing 6. **Error Handling**: Graceful degradation and logging 7. **Performance**: Provide enable/disable mechanisms for expensive operations @@ -467,7 +456,7 @@ def test_callback_triggering(): |---------|--------|---------|--------| | **Array Access** | `with mesh.access(var): var.data[...] = values` | `var.array[:, 0, 0] = values` | Direct access preferred | | **Multi-Variable** | `with mesh.access(var1, var2):` | `with uw.synchronised_array_update():` | Batch context | -| **Documentation** | Plain markdown | Quarto markdown | Enhanced features | +| **Documentation** | Plain markdown | MyST markdown (Sphinx) | Enhanced features | | **Testing** | Ad-hoc patterns | Structured fixtures | Comprehensive coverage | ## Quality Guidelines @@ -487,5 +476,5 @@ def test_callback_triggering(): ```{tip} Contributing This guide should be updated as new patterns emerge and existing patterns evolve. For questions or suggestions, please see the Contributing Guidelines or open an issue on the Underworld3 repository. -*Last updated: After NDArray migration and synchronised_array_update implementation* +*Last updated: 2026-07 (Wave E docs alignment — docstring/doc-format/coordinate sections brought in line with the UW3 Style Charter)* ``` \ No newline at end of file diff --git a/docs/developer/design/ARCHITECTURE_ANALYSIS.md b/docs/developer/design/ARCHITECTURE_ANALYSIS.md index 14158b563..546c0ba60 100644 --- a/docs/developer/design/ARCHITECTURE_ANALYSIS.md +++ b/docs/developer/design/ARCHITECTURE_ANALYSIS.md @@ -1,5 +1,7 @@ # MeshVariable Architecture Analysis +**Status**: Historical analysis (2025-11 snapshot of the MeshVariable layering). The `persistence.py` layout it describes is superseded — `EnhancedMeshVariable` now lives in `discretisation/enhanced_variables.py` and the duplicated array-view classes were consolidated. The governing data-access document is `subsystems/data-access.md`. + ## Current Architecture (2025-01-13) ### The Layered Structure diff --git a/docs/developer/design/CONSTRAINED_FREESLIP_MULTIPLIER.md b/docs/developer/design/CONSTRAINED_FREESLIP_MULTIPLIER.md index 4a9127c0e..ce5192e16 100644 --- a/docs/developer/design/CONSTRAINED_FREESLIP_MULTIPLIER.md +++ b/docs/developer/design/CONSTRAINED_FREESLIP_MULTIPLIER.md @@ -97,7 +97,7 @@ stokes.constitutive_model.Parameters.shear_viscosity_0 = mu stokes.bodyforce = buoyancy * unit_r stokes.add_dirichlet_bc((0.0, 0.0), "Lower") # no-slip inner -lam = stokes.add_constraint_bc("Upper", g=0.0) # free-slip outer (Gamma_P1) +lam = stokes.add_constraint_bc(0.0, "Upper") # free-slip outer (Gamma_P1) stokes.solve() # no constraint tuning needed topo = stokes.topography("Upper", buoyancy_scale=delta_rho_g) # h = lambda/(drho g) @@ -122,7 +122,7 @@ Key design points: - **Coupling registered once.** The boundary residual/Jacobian (`λ·n`, the AL stiffness `r(n⊗n)`, and the `uλ`/`λu` couplings) are registered a single time; nothing recompiles between solves. -- **`add_constraint_bc(boundary, g=0, normal=None, augmentation=None)`** — +- **`add_constraint_bc(conds, boundary, normal=None, augmentation=None)`** (value-first, Style Charter API conventions) — `normal` defaults to the smooth projected normals `mesh.Gamma_P1`; `augmentation` defaults to a viscosity-scaled `r = 10⁴·μ`. diff --git a/docs/developer/design/COORDINATE_MIGRATION_GUIDE.md b/docs/developer/design/COORDINATE_MIGRATION_GUIDE.md index 63d964f2f..81a8b1865 100644 --- a/docs/developer/design/COORDINATE_MIGRATION_GUIDE.md +++ b/docs/developer/design/COORDINATE_MIGRATION_GUIDE.md @@ -1,5 +1,7 @@ # Coordinate Access Migration Guide +**Status**: Historical migration guide (2025). The `mesh.data` → `mesh.X.coords` transition it documents has shipped; kept as a reference for updating old code. + **Date**: 2025-01-11 **Audience**: Developers updating Underworld3 code to use new mesh.X interface diff --git a/docs/developer/design/MATHEMATICAL_MIXIN_DESIGN.md b/docs/developer/design/MATHEMATICAL_MIXIN_DESIGN.md index ccf45b295..3e377e553 100644 --- a/docs/developer/design/MATHEMATICAL_MIXIN_DESIGN.md +++ b/docs/developer/design/MATHEMATICAL_MIXIN_DESIGN.md @@ -1,7 +1,7 @@ # MathematicalMixin Symbolic Behavior Design Document **Date**: 2025-10-26 **Author**: Claude -**Status**: Design Phase +**Status**: Implemented (`utilities/mathematical_mixin.py`) — this document is the design record (2025-10-26) --- diff --git a/docs/developer/design/ND_UNITS_BOUNDARY_CONTRACT.md b/docs/developer/design/ND_UNITS_BOUNDARY_CONTRACT.md index 8b7eb262f..31b8fb2ea 100644 --- a/docs/developer/design/ND_UNITS_BOUNDARY_CONTRACT.md +++ b/docs/developer/design/ND_UNITS_BOUNDARY_CONTRACT.md @@ -1,5 +1,7 @@ # The non-dimensional ↔ units boundary (contract) +**Status**: Contract / reference (current) — PR #278 (2026-06-24). Documents the shipped ND ↔ units boundary (`non_dimensionalise` / `dimensionalise` in `units.py`, `.coords_nd`). + This page defines **where the units system stops and the non-dimensional (ND) solver world begins** in Underworld3, for both users and developers. It is the companion "rules of engagement" to the diff --git a/docs/developer/design/WHY_UNITS_NOT_DIMENSIONALITY.md b/docs/developer/design/WHY_UNITS_NOT_DIMENSIONALITY.md index aaeb8f1ae..ca0a63665 100644 --- a/docs/developer/design/WHY_UNITS_NOT_DIMENSIONALITY.md +++ b/docs/developer/design/WHY_UNITS_NOT_DIMENSIONALITY.md @@ -1,5 +1,7 @@ # Why "Units" Not "Dimensionality" - User-Facing Terminology +**Status**: Decision record (2025) — the user-facing "units" terminology is the shipped convention. + **Date:** 2025-01-07 **Decision:** Merge DimensionalityMixin INTO UnitAwareMixin, keep "units" terminology **Reason:** User communication and API consistency diff --git a/docs/developer/design/fault-refinement-simplification.md b/docs/developer/design/fault-refinement-simplification.md index 808ad0844..459d9da5c 100644 --- a/docs/developer/design/fault-refinement-simplification.md +++ b/docs/developer/design/fault-refinement-simplification.md @@ -1,5 +1,7 @@ # Fault refinement — the simplification +**Status**: Implemented — design note (2026-05-28). `smooth_mesh_interior` / `metric_density_from_gradient` (`meshing/smoothing.py`) and `fault_comb_metric` (`meshing/surfaces.py`) are in the tree. + ```{note} Design note, 2026-05-28. Captures the convergence after the feature/elliptic-ma fault-meshing work: one mover, one metric form, one diff --git a/docs/developer/design/fmg-checkpoint-hierarchy.md b/docs/developer/design/fmg-checkpoint-hierarchy.md index 67bed838d..78df1fd48 100644 --- a/docs/developer/design/fmg-checkpoint-hierarchy.md +++ b/docs/developer/design/fmg-checkpoint-hierarchy.md @@ -1,5 +1,6 @@ --- title: "Persisting the FMG mesh hierarchy across checkpoints" +status: "Implemented (2026-06-11, commit 3cd73cde) — FMG coarse-hierarchy sidecar restore in discretisation_mesh.py" --- # Persisting the geometric-multigrid hierarchy across checkpoints diff --git a/docs/developer/design/in_memory_checkpoint_design.md b/docs/developer/design/in_memory_checkpoint_design.md index b705efd1c..243eb807c 100644 --- a/docs/developer/design/in_memory_checkpoint_design.md +++ b/docs/developer/design/in_memory_checkpoint_design.md @@ -1,5 +1,7 @@ # In-memory checkpoint as a general UW3 capability +**Status**: Design note (2026-05-11) — not implemented. Audit complete, implementation not started (see the Status section at the end of this document). + Design note. Spun off from the deformable-surface architectural discussion (2026-05-11) as a self-contained capability that is bounded, useful in its own right, and not specifically tied to free-surface code. diff --git a/docs/developer/design/jacobian-consistent-tangent.md b/docs/developer/design/jacobian-consistent-tangent.md index c14477ea6..eff27c366 100644 --- a/docs/developer/design/jacobian-consistent-tangent.md +++ b/docs/developer/design/jacobian-consistent-tangent.md @@ -1,5 +1,7 @@ # Consistent Jacobian tangent for nonlinear (viscoplastic) solves +**Status**: Implemented — PR #258 (2026-07-02): opt-in `solver.consistent_jacobian`, default off (Picard tangent unchanged). + ## The bug The SNES Jacobian assembly in `src/underworld3/cython/petsc_generic_snes_solvers.pyx` diff --git a/docs/developer/design/mesh-adaptation-formulation.md b/docs/developer/design/mesh-adaptation-formulation.md index 25334f095..40f03cf23 100644 --- a/docs/developer/design/mesh-adaptation-formulation.md +++ b/docs/developer/design/mesh-adaptation-formulation.md @@ -1,5 +1,7 @@ # Mesh adaptation by metric-driven node redistribution — mathematical formulation +**Status**: Reference (current, 2026-05) — the mathematical formulation for the implemented `smooth_mesh_interior` family (`meshing/smoothing.py`). + > **Scope.** This is the self-contained *mathematical* reference for the > topology-preserving mesh-adaptation family in UW3 > (`uw.meshing.smooth_mesh_interior`). It derives the three solution diff --git a/docs/developer/design/petsc-dmplex-checkpoint-reload-plan.md b/docs/developer/design/petsc-dmplex-checkpoint-reload-plan.md index 2df11c647..8d139c021 100644 --- a/docs/developer/design/petsc-dmplex-checkpoint-reload-plan.md +++ b/docs/developer/design/petsc-dmplex-checkpoint-reload-plan.md @@ -1,5 +1,7 @@ # PETSc DMPlex Checkpoint Reload Plan +**Status**: Implemented — PR #146 (2026-05-20); the exact-reload path shipped as `write_timestep(..., petsc_reload=True)` (`discretisation_mesh.py`). + ## Commit And Test Workflow Use small, meaningful commits while implementing this work. diff --git a/docs/developer/design/snesfas-feasibility.md b/docs/developer/design/snesfas-feasibility.md index 2ec6ec713..b1ecc6f4b 100644 --- a/docs/developer/design/snesfas-feasibility.md +++ b/docs/developer/design/snesfas-feasibility.md @@ -1,5 +1,6 @@ --- title: "SNESFAS (nonlinear multigrid) feasibility in Underworld3" +status: "Investigation record (preserved via PR #245, 2026-06-18); the production geometric-MG path is custom prolongation (PR #290)" --- # SNESFAS feasibility spike diff --git a/docs/developer/design/snesfas-vanka-feasibility-study.md b/docs/developer/design/snesfas-vanka-feasibility-study.md index 37dee2215..2db8fe33c 100644 --- a/docs/developer/design/snesfas-vanka-feasibility-study.md +++ b/docs/developer/design/snesfas-vanka-feasibility-study.md @@ -1,5 +1,6 @@ --- title: "Feasibility study: a scalable saddle-point smoother for Stokes SNESFAS" +status: "Investigation record (preserved via PR #245, 2026-06-18); prototype only — FAS wrap / UW3 API not landed" --- # Feasibility study — scalable saddle smoother for Stokes FAS diff --git a/docs/developer/design/submesh-solver-architecture.md b/docs/developer/design/submesh-solver-architecture.md index 70ea7e514..6219f533d 100644 --- a/docs/developer/design/submesh-solver-architecture.md +++ b/docs/developer/design/submesh-solver-architecture.md @@ -1,5 +1,7 @@ # Submesh Solver Architecture: Multi-Domain Equation Systems +**Status**: Prototype (2026-04 – 2026-05). The subdomain (`extract_region`) and surface (`extract_surface`) flavours exist in `discretisation_mesh.py`; the resolution-level `coarsened_companion` flavour is not implemented. + ## Context Underworld3 needs to support solving different equations on different subsets of a mesh while maintaining a unified field representation. Use cases include: diff --git a/docs/developer/guides/release-process.md b/docs/developer/guides/release-process.md index 8eb7d5249..2405ccb54 100644 --- a/docs/developer/guides/release-process.md +++ b/docs/developer/guides/release-process.md @@ -133,6 +133,33 @@ Step 8 is deliberately left to a human so the review gate on `main` is never bypassed. CI (`.github/workflows/release.yml`) publishes the GitHub Release from the committed `docs/release-notes/vX.Y.0.md`. +### Documentation freshness sweeps (before step 6) + +Two "pull" documents go stale silently between releases; refresh both as part +of every release cycle: + +1. **Docstring review queue** — regenerate and commit + `docs/docstrings/review_queue.md` (+ `inventory.json`): + + ```bash + python scripts/docstring_sweep.py 'src/underworld3/**/*.py' 'src/underworld3/**/*.pyx' + ``` + + A stale queue misdirects documentation work both ways: it re-flags items + that have since been documented and omits API added since the last sweep. + +2. **Quarterly changelog** — sweep the merge history since the changelog's + last entry and backfill `docs/developer/CHANGELOG.md` at its conceptual + granularity (grouped by subsystem, not per PR): + + ```bash + git log --first-parent --oneline ..development + ``` + +Both documents rotted over the January–June 2026 development burst (findings +DOC-02 / DOC-03 in `docs/reviews/2026-07/DOCS-STANDARDS-COHERENCE.md`); this +checklist item exists so that cannot happen unnoticed again. + ## Release notes generation Steps 4–6 auto-populate two sections of the notes from the gate: diff --git a/docs/developer/index.md b/docs/developer/index.md index 8fe04584b..24523b555 100644 --- a/docs/developer/index.md +++ b/docs/developer/index.md @@ -25,6 +25,22 @@ Underworld3 is a Python library for computational geodynamics and geophysical mo - **Need coding standards?** → Reference [Style Guide](UW3_Style_and_Patterns_Guide.md) ``` +## Documentation Authority Map + +One governing document per topic (this records the [UW3 Style Charter](UW3_STYLE_CHARTER.md) +§10 authority map — the Charter itself wins on any conflict). Other documents on the +same topic are reference or historical material subordinate to the governing document. + +| Topic | Governing document | +|-------|--------------------| +| Coding style & API conventions | [UW3 Style Charter](UW3_STYLE_CHARTER.md) (detailed reference: [Style Guide](UW3_Style_and_Patterns_Guide.md)) | +| Data access | [subsystems/data-access.md](subsystems/data-access.md) (internals reference: [NDArray System](UW3_Developers_NDArrays.md)) | +| Units | [design/UNITS_SIMPLIFIED_DESIGN_2025-11.md](design/UNITS_SIMPLIFIED_DESIGN_2025-11.md) | +| Testing tiers | [TESTING-RELIABILITY-SYSTEM.md](TESTING-RELIABILITY-SYSTEM.md) | +| Branching & releases | [guides/branching-strategy.md](guides/branching-strategy.md) | +| Docstring format | NumPy/Sphinx with RST `:math:` — Charter §6 and the [Style Guide docstring section](UW3_Style_and_Patterns_Guide.md) | +| Documentation file format | MyST Markdown (`.md`) for Sphinx — CLAUDE.md "Documentation Requests" section | + ## Documentation Structure This documentation is organized into focused sections: @@ -98,6 +114,7 @@ This developer documentation covers Underworld3 version 0.99+. It includes both guides/development-setup guides/contributing +UW3_STYLE_CHARTER UW3_Style_and_Patterns_Guide ``` diff --git a/docs/docstrings/inventory.json b/docs/docstrings/inventory.json index a07245e4b..1fe1b6294 100644 --- a/docs/docstrings/inventory.json +++ b/docs/docstrings/inventory.json @@ -3,16 +3,16 @@ "name": "SolverBaseClass", "kind": "class", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 22, + "line": 52, "signature": "class SolverBaseClass(uw_object):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n The Generic `Solver` is used to build the `SNES Solvers`\n - `SNES_Scalar`\n - `SNES_Vector`\n - `SNES_Stokes`\n\n This class is not intended to be used directly\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true @@ -21,16 +21,15 @@ "name": "SNES_Scalar", "kind": "class", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 807, + "line": 2327, "signature": "class SNES_Scalar(SolverBaseClass):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n General scalar equation solver using PETSc SNES.\n\n Solves the scalar conservation problem for unknown :math:`u`:\n\n .. math::\n\n \\nabla \\cdot \\mathbf{F}(u, \\nabla u, \\dot{u}, \\nabla\\dot{u})\n - f(u, \\nabla u, \\dot{u}, \\nabla\\dot{u}) = 0\n\n where :math:`f` is a source term, :math:`\\mathbf{F}` is a flux term relating\n :math:`u` to its gradients :math:`\\nabla u`, and :math:`\\dot{u}` is the\n Lagrangian time derivative.\n\n The unknown :math:`u` is a scalar mesh variable, and :math:`f`, :math:`\\mathbf{F}`\n are arbitrary sympy expressions of mesh coordinate variables.\n\n This class is the base layer for building solvers that translate physical\n conservation laws into this general mathematical form.\n\n Parameters\n ----------\n mesh : underworld3.discretisation.Mesh\n The computational mesh.\n u_Field : MeshVariable, optional\n Pre-existing scalar field variable. If None, creates a new variable.\n degree : int, default=2\n Polynomial degree for finite element discretization.\n verbose : bool, default=False\n Enable verbose solver output (monitors convergence).\n DuDt : SemiLagrangian or Lagrangian, optional\n Time derivative handler for the unknown (advection-diffusion problems).\n DFDt : SemiLagrangian or Lagrangian, optional\n Time derivative handler for flux (viscoelastic problems).\n\n Attributes\n ----------\n u : MeshVariable\n The scalar unknown being solved for.\n F0 : UWexpression\n Source/force term :math:`f`.\n F1 : UWexpression\n Flux term :math:`\\mathbf{F}`.\n constitutive_model : Constitutive_Model\n Material model defining flux-gradient relationship.\n tolerance : float\n Solver convergence tolerance.\n\n Examples\n --------\n >>> import underworld3 as uw\n >>> mesh = uw.meshing.StructuredQuadBox(elementRes=(16, 16))\n >>> poisson = uw.systems.Poisson(mesh)\n >>> poisson.constitutive_model = uw.constitutive_models.DiffusionModel\n >>> poisson.constitutive_model.Parameters.diffusivity = 1.0\n >>> poisson.f = 1.0 # Source term\n >>> poisson.add_dirichlet_bc(0.0, \"Bottom\")\n >>> poisson.solve()\n\n See Also\n --------\n SNES_Vector : For vector-valued equations.\n SNES_Stokes_SaddlePt : For coupled velocity-pressure (Stokes) problems.\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true @@ -39,16 +38,32 @@ "name": "SNES_Vector", "kind": "class", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1401, + "line": 3263, "signature": "class SNES_Vector(SolverBaseClass):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n General vector equation solver using PETSc SNES.\n\n Solves the vector conservation problem for unknown :math:`\\mathbf{u}`:\n\n .. math::\n\n \\nabla \\cdot \\mathbf{F}(\\mathbf{u}, \\nabla \\mathbf{u}, \\dot{\\mathbf{u}},\n \\nabla\\dot{\\mathbf{u}}) - \\mathbf{f}(\\mathbf{u}, \\nabla \\mathbf{u},\n \\dot{\\mathbf{u}}, \\nabla\\dot{\\mathbf{u}}) = 0\n\n where :math:`\\mathbf{f}` is a source term, :math:`\\mathbf{F}` is a flux term\n relating :math:`\\mathbf{u}` to its gradients :math:`\\nabla \\mathbf{u}`, and\n :math:`\\dot{\\mathbf{u}}` is the Lagrangian time derivative.\n\n The unknown :math:`\\mathbf{u}` is a vector mesh variable, and :math:`\\mathbf{f}`,\n :math:`\\mathbf{F}` are arbitrary sympy expressions that may include mesh\n coordinates and other mesh/swarm variables.\n\n This class is the base layer for building solvers that translate physical\n conservation laws into this general mathematical form.\n\n Parameters\n ----------\n mesh : underworld3.discretisation.Mesh\n The computational mesh.\n u_Field : MeshVariable, optional\n Pre-existing vector field variable. If None, creates a new variable.\n degree : int, default=2\n Polynomial degree for finite element discretization.\n verbose : bool, default=False\n Enable verbose solver output (monitors convergence).\n DuDt : SemiLagrangian or Lagrangian, optional\n Time derivative handler for the unknown.\n DFDt : SemiLagrangian or Lagrangian, optional\n Time derivative handler for flux.\n\n Attributes\n ----------\n u : MeshVariable\n The vector unknown being solved for.\n F0 : UWexpression\n Source/force term :math:`\\mathbf{f}`.\n F1 : UWexpression\n Flux term :math:`\\mathbf{F}`.\n constitutive_model : Constitutive_Model\n Material model defining flux-gradient relationship.\n tolerance : float\n Solver convergence tolerance.\n\n Examples\n --------\n >>> import underworld3 as uw\n >>> mesh = uw.meshing.StructuredQuadBox(elementRes=(16, 16))\n >>> # Vector projection solver\n >>> proj = uw.systems.Vector_Projection(mesh)\n >>> proj.uw_function = some_vector_expression\n >>> proj.solve()\n\n See Also\n --------\n SNES_Scalar : For scalar-valued equations.\n SNES_Stokes_SaddlePt : For coupled velocity-pressure (Stokes) problems.\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "SNES_MultiComponent", + "kind": "class", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4334, + "signature": "class SNES_MultiComponent(SolverBaseClass):", + "parameters": [], + "returns": null, + "existing_docstring": "\n General multi-component equation solver using PETSc SNES.\n\n Generalises :class:`SNES_Vector` to an arbitrary number of DOF\n components per node, decoupled from ``mesh.dim``. Solves for an\n N-component unknown :math:`\\mathbf{u}` (stored as a ``(1, Nc)`` row\n matrix variable) with per-component residual and flux terms.\n\n Unlike :class:`SNES_Vector`, the components of :math:`\\mathbf{u}` have\n no inherent physical meaning as a spatial vector \u2014 they are N\n independent scalar DOFs sharing one DM. Cross-component coupling is\n allowed through ``F0`` and ``F1`` but not required; the primary use\n case (multi-component projection) is block-diagonal.\n\n Parameters\n ----------\n mesh : underworld3.discretisation.Mesh\n u_Field : MeshVariable, optional\n Pre-existing ``(1, n_components)`` MATRIX variable. If ``None``,\n one is created.\n n_components : int\n Number of scalar DOF components per node. Required when\n ``u_Field`` is None; otherwise inferred from ``u_Field.shape``.\n degree : int, default=2\n verbose : bool, default=False\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true @@ -57,16 +72,15 @@ "name": "SNES_Stokes_SaddlePt", "kind": "class", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2092, + "line": 4998, "signature": "class SNES_Stokes_SaddlePt(SolverBaseClass):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n Saddle point equation solver for constrained problems using PETSc SNES.\n\n Solves the constrained vector conservation problem for unknown :math:`\\mathbf{u}`\n with constraint parameter :math:`p`:\n\n .. math::\n\n \\nabla \\cdot \\mathbf{F}(\\mathbf{u}, p, \\nabla \\mathbf{u}, \\nabla p,\n \\dot{\\mathbf{u}}, \\nabla\\dot{\\mathbf{u}}) - \\mathbf{f}(\\mathbf{u}, p,\n \\nabla \\mathbf{u}, \\nabla p, \\dot{\\mathbf{u}}, \\nabla\\dot{\\mathbf{u}}) = 0\n\n .. math::\n\n f_p(\\mathbf{u}, \\nabla \\mathbf{u}, \\dot{\\mathbf{u}}, \\nabla\\dot{\\mathbf{u}}) = 0\n\n where :math:`\\mathbf{f}` is a source term, :math:`\\mathbf{F}` is a flux term\n relating :math:`\\mathbf{u}` to its gradients, :math:`\\dot{\\mathbf{u}}` is\n the Lagrangian time derivative, and :math:`f_p` expresses the constraints\n on :math:`\\mathbf{u}` enforced by parameter :math:`p`.\n\n The unknown :math:`\\mathbf{u}` is a vector mesh variable and :math:`p` is a\n scalar mesh variable. The terms :math:`\\mathbf{f}`, :math:`\\mathbf{F}`, and\n :math:`f_p` are arbitrary sympy expressions that may include mesh coordinates\n and other mesh/swarm variables.\n\n This class is the base layer for building solvers that translate physical\n conservation laws into this general mathematical form.\n\n Parameters\n ----------\n mesh : underworld3.discretisation.Mesh\n The computational mesh.\n velocityField : MeshVariable, optional\n Pre-existing velocity field. If None, creates a new variable.\n pressureField : MeshVariable, optional\n Pre-existing pressure field. If None, creates a new variable.\n degree : int, default=2\n Polynomial degree for velocity (pressure is degree-1).\n p_continuous : bool, default=True\n Whether pressure field is continuous (True) or discontinuous (False).\n verbose : bool, default=False\n Enable verbose solver output.\n DuDt : SemiLagrangian or Lagrangian, optional\n Time derivative handler for velocity (viscoelastic problems).\n DFDt : SemiLagrangian or Lagrangian, optional\n Time derivative handler for stress (viscoelastic problems).\n\n Attributes\n ----------\n u : MeshVariable\n Velocity field being solved for.\n p : MeshVariable\n Pressure (Lagrange multiplier) field.\n F0 : UWexpression\n Body force term :math:`\\mathbf{f}`.\n F1 : UWexpression\n Stress/flux term :math:`\\mathbf{F}`.\n PF0 : UWexpression\n Constraint term :math:`f_p` (typically incompressibility).\n constitutive_model : Constitutive_Model\n Viscous/viscoelastic material model.\n tolerance : float\n Solver convergence tolerance.\n bodyforce : sympy.Matrix\n Body force vector (e.g., gravity).\n penalty : float\n Penalty parameter for augmented Lagrangian methods.\n\n Examples\n --------\n >>> import underworld3 as uw\n >>> mesh = uw.meshing.StructuredQuadBox(elementRes=(32, 32))\n >>> stokes = uw.systems.Stokes(mesh)\n >>> stokes.constitutive_model = uw.constitutive_models.ViscousFlowModel\n >>> stokes.constitutive_model.Parameters.viscosity = 1.0\n >>> stokes.bodyforce = sympy.Matrix([0, -1]) # Gravity\n >>> stokes.add_dirichlet_bc([0.0, 0.0], \"Bottom\")\n >>> stokes.add_dirichlet_bc([None, 0.0], \"Top\")\n >>> stokes.solve()\n >>> velocity = stokes.u.array[:, 0, :]\n >>> pressure = stokes.p.array[:, 0, 0]\n\n See Also\n --------\n SNES_Scalar : For scalar-valued equations.\n SNES_Vector : For vector-valued equations without constraints.\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true @@ -109,11 +123,11 @@ "name": "SNES_Poisson", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 154, + "line": 160, "signature": "class SNES_Poisson", "parameters": [], "returns": null, - "existing_docstring": "Poisson equation solver.\n\nProvides a discrete representation of the Poisson equation:\n\n.. math::\n\n \\nabla \\cdot \\left[ \\boldsymbol{\\kappa} \\nabla u \\right] = f\n\nwhere :math:`\\mathbf{F} = \\boldsymbol{\\kappa} \\nabla u` relates the flux to\ngradients in the unknown :math:`u`.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable, optional\n Pre-existing mesh variable for the solution. If None, one is created.\nverbose : bool, optional\n Enable verbose output during solve.\ndegree : int, optional\n Polynomial degree for the solution field (default: 2).\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for time-dependent problems.\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for the flux.\n\nAttributes\n----------\nu : MeshVariable\n The unknown scalar field.\nconstitutive_model : DiffusionModel\n Provides the diffusivity tensor :math:`\\kappa`. Set to one of the\n scalar ``uw.constitutive_models`` classes. Can be constant, spatially\n varying, non-linear, or anisotropic.\nf : sympy.Expr\n Volumetric source term.", + "existing_docstring": "Poisson equation solver.\n\nProvides a discrete representation of the Poisson equation:\n\n.. math::\n\n - \\nabla \\cdot \\left[ \\boldsymbol{\\kappa} \\nabla u \\right] = f\n\nwhere :math:`\\mathbf{F} = \\boldsymbol{\\kappa} \\nabla u` relates the flux to\ngradients in the unknown :math:`u`.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable, optional\n Pre-existing mesh variable for the solution. If None, one is created.\ndegree : int, optional\n Polynomial degree for the solution field (default: 2).\nverbose : bool, optional\n Enable verbose output during solve.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for time-dependent problems.\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for the flux.\n\nNotes\n-----\nThe constructor follows the family-wide parameter order\n``(mesh, u_Field, degree, verbose, ...)`` (Style Charter, API\nconventions); SNES_Poisson historically inverted ``(verbose, degree)``.\nA legacy positional call is detected by ``type(degree) is bool`` (a\npolynomial degree is never a bool) and shimmed with one\nDeprecationWarning.", "harvested_comments": [], "status": "partial", "needs": [ @@ -126,26 +140,60 @@ "name": "SNES_Darcy", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 333, + "line": 360, "signature": "class SNES_Darcy", "parameters": [], "returns": null, - "existing_docstring": "Darcy flow equation solver for groundwater problems.\n\nProvides a discrete representation of the groundwater flow equations:\n\n.. math::\n\n \\underbrace{S_s \\frac{\\partial h}{\\partial t}}_{\\dot{u}}\n - \\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\kappa} \\nabla h\n - \\boldsymbol{s} \\right]}_{\\mathbf{F}}\n = \\underbrace{W}_{h}\n\nThe flux term :math:`\\mathbf{F}` relates the effective velocity to\npressure gradients:\n\n.. math::\n\n \\boldsymbol{v} = \\boldsymbol{\\kappa} \\nabla h - \\boldsymbol{s}\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nh_Field : MeshVariable, optional\n Mesh variable for hydraulic head. Created automatically if not provided.\nv_Field : MeshVariable, optional\n Mesh variable for Darcy velocity. Created automatically if not provided.\ndegree : int, default=2\n Polynomial degree for the finite element discretization.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : optional\n Time derivative operator for the unknown.\nDFDt : optional\n Time derivative operator for the flux.\n\nAttributes\n----------\nh : MeshVariable\n The hydraulic head unknown.\nv : MeshVariable\n The Darcy velocity field.\ns : sympy.Expr\n Source term for pressure gradients (e.g., :math:`\\rho g`).\nSs : sympy.Expr\n Specific storage coefficient.\n\nNotes\n-----\n- The unknown is :math:`h`, the hydraulic head\n- The permeability tensor :math:`\\kappa` is set via the ``constitutive_model``\n property using one of the ``uw.constitutive_models`` classes\n- :math:`W` is a pressure source term\n- :math:`S_s` is the specific storage coefficient\n- The time-dependent term :math:`\\dot{f}` is not implemented in this version\n- The solver returns both the primary field and the Darcy flux (mean-flow velocity)\n\nSee Also\n--------\nSNES_Poisson : Related diffusion-only solver.\nuw.constitutive_models.DarcyFlowModel : Constitutive model for Darcy flow.", + "existing_docstring": "Darcy flow equation solver for groundwater problems.\n\nProvides a discrete representation of the groundwater flow equations:\n\n.. math::\n\n \\underbrace{S_s \\frac{\\partial h}{\\partial t}}_{\\dot{u}}\n - \\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\kappa} \\left(\n \\nabla h - \\boldsymbol{s} \\right) \\right]}_{\\mathbf{F}}\n = \\underbrace{W}_{h}\n\nThe physical Darcy velocity is minus the assembly flux\n:math:`\\mathbf{F} = \\boldsymbol{\\kappa}(\\nabla h - \\boldsymbol{s})`\n(flow runs *down* the head gradient):\n\n.. math::\n\n \\boldsymbol{v} = - \\boldsymbol{\\kappa} \\left( \\nabla h - \\boldsymbol{s} \\right)\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nh_Field : MeshVariable, optional\n Mesh variable for hydraulic head. Created automatically if not provided.\nv_Field : MeshVariable, optional\n Mesh variable for Darcy velocity. Created automatically if not provided.\ndegree : int, default=2\n Polynomial degree for the finite element discretization.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : optional\n Time derivative operator for the unknown.\nDFDt : optional\n Time derivative operator for the flux.\n\nNotes\n-----\n- The unknown is :math:`h`, the hydraulic head\n- The permeability tensor :math:`\\kappa` is set via the ``constitutive_model``\n property using one of the ``uw.constitutive_models`` classes\n- :math:`W` is a pressure source term\n- :math:`S_s` is the specific storage coefficient\n- The time-dependent term :math:`\\dot{f}` is not implemented in this version\n- The solver returns both the primary field and the Darcy flux (mean-flow velocity)\n\nSee Also\n--------\nSNES_Poisson : Related diffusion-only solver.\nuw.constitutive_models.DarcyFlowModel : Constitutive model for Darcy flow.", "harvested_comments": [], "status": "complete", "needs": [], "parent_class": null, "is_public": true }, + { + "name": "SNES_TransientDarcy", + "kind": "class", + "file": "src/underworld3/systems/solvers.py", + "line": 582, + "signature": "class SNES_TransientDarcy", + "parameters": [], + "returns": null, + "existing_docstring": "Transient Darcy flow solver for time-dependent groundwater problems.\n\nSolves the transient groundwater flow equation:\n\n.. math::\n\n S_s \\frac{\\partial h}{\\partial t}\n - \\nabla \\cdot \\left[ K (\\nabla h - \\mathbf{s}) \\right] = f\n\nwhere :math:`S_s` is the specific storage (constant), :math:`K` is the\nhydraulic conductivity, :math:`\\mathbf{s}` is the body force (gravity),\nand :math:`f` is a source/sink term.\n\nInherits velocity projection from :class:`SNES_Darcy`.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nh_Field : MeshVariable, optional\n Mesh variable for hydraulic head.\nv_Field : MeshVariable, optional\n Mesh variable for Darcy velocity.\norder : int, default=1\n Time integration order (BDF/Adams-Moulton history depth).\ntheta : float, default=0.5\n Implicit time weighting (0=explicit, 0.5=Crank-Nicolson, 1=implicit).\ndegree : int, default=2\n Polynomial degree for finite element discretization.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : optional\n Time derivative operator for the unknown.\nDFDt : optional\n Time derivative operator for the flux.\n\nAttributes\n----------\nstorage : sympy.Expr\n Specific storage :math:`S_s` (default 1).\ndelta_t : UWexpression\n Current timestep.\n\nSee Also\n--------\nSNES_Darcy : Steady-state parent solver.\nSNES_Richards : Nonlinear extension for unsaturated flow.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "SNES_Richards", + "kind": "class", + "file": "src/underworld3/systems/solvers.py", + "line": 861, + "signature": "class SNES_Richards", + "parameters": [], + "returns": null, + "existing_docstring": "Richards equation solver for variably-saturated porous media flow.\n\nTwo formulations are supported:\n\n**Mixed form** (mass-conservative, preferred) \u2014 set ``water_content``:\n\n.. math::\n\n \\frac{\\partial \\theta}{\\partial t}\n - \\nabla \\cdot \\left[ K(\\psi) (\\nabla \\psi - \\mathbf{s}) \\right] = f\n\ndiscretised as :math:`(\\theta(\\psi^{n+1}) - \\theta(\\psi^n)) / \\Delta t`,\nwhich is exactly conservative by construction (Celia et al., 1990).\n\n**Head-based form** (backward compatible) \u2014 set ``capacity``:\n\n.. math::\n\n C(\\psi) \\frac{\\partial \\psi}{\\partial t}\n - \\nabla \\cdot \\left[ K(\\psi) (\\nabla \\psi - \\mathbf{s}) \\right] = f\n\nwhere :math:`C(\\psi) = d\\theta/d\\psi` is the specific moisture capacity.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\npsi_Field : MeshVariable, optional\n Mesh variable for pressure head :math:`\\psi`.\nv_Field : MeshVariable, optional\n Mesh variable for Darcy velocity.\norder : int, default=1\n Time integration order.\ntheta : float, default=0.5\n Implicit time weighting.\ndegree : int, default=2\n Polynomial degree.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : optional\n Time derivative operator for the unknown.\nDFDt : optional\n Time derivative operator for the flux.\n\nAttributes\n----------\nwater_content : sympy.Expr or None\n Water content function :math:`\\theta(\\psi)` for the mixed form.\n When set, the storage term uses\n :math:`(\\theta(\\psi^{n+1}) - \\theta(\\psi^n)) / \\Delta t`.\n Takes precedence over ``capacity`` if both are set.\ncapacity : sympy.Expr\n Specific moisture capacity :math:`C(\\psi)` for the head-based form.\n Used only when ``water_content`` is None.\npsi : MeshVariable\n Alias for ``self.u`` (pressure head).\n\nSee Also\n--------\nSNES_TransientDarcy : Linear parent solver.\nunderworld3.utilities.retention_curves : Van Genuchten and Gardner functions.\n\nExamples\n--------\nMixed form (preferred):\n\n>>> from underworld3.utilities.retention_curves import (\n... van_genuchten_theta, van_genuchten_K,\n... )\n>>> richards = uw.systems.Richards(mesh, psi_Field=psi, v_Field=v)\n>>> richards.constitutive_model = uw.constitutive_models.DarcyFlowModel\n>>> richards.constitutive_model.Parameters.permeability = van_genuchten_K(\n... psi.sym[0], Ks=1e-4, alpha=3.35, n=2.0,\n... )\n>>> richards.water_content = van_genuchten_theta(\n... psi.sym[0], theta_r=0.045, theta_s=0.43, alpha=3.35, n=2.0,\n... )\n\nHead-based form (backward compatible):\n\n>>> from underworld3.utilities.retention_curves import van_genuchten_C\n>>> richards.capacity = van_genuchten_C(\n... psi.sym[0], theta_r=0.045, theta_s=0.43, alpha=3.35, n=2.0,\n... )", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, { "name": "SNES_Stokes", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 558, + "line": 1047, "signature": "class SNES_Stokes", "parameters": [], "returns": null, - "existing_docstring": "Stokes equation solver for incompressible viscous flow.\n\nThis class provides functionality for a discrete representation\nof the Stokes flow equations assuming an incompressibility\n(or near-incompressibility) constraint.\n\nThe momentum equation is:\n\n.. math::\n\n -\\nabla \\cdot \\left[ \\boldsymbol{\\tau} - p \\mathbf{I} \\right] = \\mathbf{f}\n\nwith the incompressibility constraint:\n\n.. math::\n\n \\nabla \\cdot \\mathbf{u} = 0\n\nThe flux term is a deviatoric stress (:math:`\\boldsymbol{\\tau}`) related to velocity\ngradients (:math:`\\nabla \\mathbf{u}`) through a viscosity tensor :math:`\\eta`, and a\nvolumetric (pressure) part :math:`p`:\n\n.. math::\n\n \\boldsymbol{\\tau} = \\frac{\\eta}{2}\\left( \\nabla \\mathbf{u} + \\nabla \\mathbf{u}^T \\right)\n\nThe constraint equation gives incompressible flow by default but can be set\nto any function of the unknown :math:`\\mathbf{u}` and :math:`\\nabla \\cdot \\mathbf{u}`.\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The computational mesh.\nvelocityField : uw.discretisation.MeshVariable, optional\n Pre-existing velocity field. If None, one is created automatically.\npressureField : uw.discretisation.MeshVariable, optional\n Pre-existing pressure field. If None, one is created automatically.\ndegree : int, optional\n Polynomial degree for velocity interpolation. Default is 2.\np_continuous : bool, optional\n If True (default), pressure is continuous. Set False for discontinuous pressure.\nverbose : bool, optional\n Enable verbose output during solving. Default is False.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Material derivative operator for velocity (used in derived classes).\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Material derivative operator for flux (used in viscoelastic models).\n\nAttributes\n----------\nu : MeshVariable\n The velocity field (accessed via ``solver.Unknowns.u``).\np : MeshVariable\n The pressure field (accessed via ``solver.Unknowns.p``).\nbodyforce : UWexpression\n Volumetric body force vector :math:`\\mathbf{f}`.\nconstitutive_model : ConstitutiveModel\n Viscosity model providing the stress-strain relationship.\npenalty : UWexpression\n Augmented Lagrangian penalty parameter :math:`\\lambda`.\nsaddle_preconditioner : sympy.Expr\n Preconditioner for the saddle point system (default: :math:`1/\\eta`).\nconstraints : sympy.Matrix\n Constraint equation(s), default is :math:`\\nabla \\cdot \\mathbf{u}`.\n\nNotes\n-----\n**Viscosity model**: The viscosity tensor :math:`\\boldsymbol{\\eta}` is provided by\nsetting the ``constitutive_model`` property to one of the ``uw.constitutive_models``\nclasses. It may be constant, spatially varying, non-linear, or anisotropic.\n\n**Augmented Lagrangian**: Setting ``penalty`` to a non-zero value adds\n:math:`\\lambda \\nabla \\cdot \\mathbf{u}` to the weak form, improving convergence\nfor incompressible flow (in addition to the constraint equation).\n\n**Mixed finite elements**: The pressure field interpolation order determines\nthe integration order of the mixed method and is typically lower than the\nvelocity field order.\n\n**Viscoelastic models**: For viscoelastic behaviour, the flux term contains\nstress history tracked on a particle swarm. See :class:`SNES_VE_Stokes`.\n\nSee Also\n--------\nSNES_VE_Stokes : Viscoelastic Stokes solver with flux history.\nSNES_NavierStokes : Navier-Stokes solver with inertial terms.\nuw.constitutive_models : Available viscosity models.\n\nExamples\n--------\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(0,0), maxCoords=(1,1), cellSize=0.1)\n>>> stokes = uw.systems.Stokes(mesh, degree=2)\n>>> stokes.constitutive_model = uw.constitutive_models.ViscousFlowModel()\n>>> stokes.constitutive_model.Parameters.viscosity = 1.0\n>>> stokes.bodyforce = [0, -1] # gravity\n>>> stokes.solve()", + "existing_docstring": "Stokes equation solver for incompressible viscous flow.\n\nThis class provides functionality for a discrete representation\nof the Stokes flow equations assuming an incompressibility\n(or near-incompressibility) constraint.\n\nThe momentum equation is:\n\n.. math::\n\n -\\nabla \\cdot \\left[ \\boldsymbol{\\tau} - p \\mathbf{I} \\right] = \\mathbf{f}\n\nwith the incompressibility constraint:\n\n.. math::\n\n \\nabla \\cdot \\mathbf{u} = 0\n\nThe flux term is a deviatoric stress (:math:`\\boldsymbol{\\tau}`) related to velocity\ngradients (:math:`\\nabla \\mathbf{u}`) through a viscosity tensor :math:`\\eta`, and a\nvolumetric (pressure) part :math:`p`:\n\n.. math::\n\n \\boldsymbol{\\tau} = \\frac{\\eta}{2}\\left( \\nabla \\mathbf{u} + \\nabla \\mathbf{u}^T \\right)\n\nThe constraint equation gives incompressible flow by default but can be set\nto any function of the unknown :math:`\\mathbf{u}` and :math:`\\nabla \\cdot \\mathbf{u}`.\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The computational mesh.\nvelocityField : uw.discretisation.MeshVariable, optional\n Pre-existing velocity field. If None, one is created automatically.\npressureField : uw.discretisation.MeshVariable, optional\n Pre-existing pressure field. If None, one is created automatically.\ndegree : int, optional\n Polynomial degree for velocity interpolation. Default is 2.\np_continuous : bool, optional\n If True (default), pressure is continuous. Set False for discontinuous pressure.\nverbose : bool, optional\n Enable verbose output during solving. Default is False.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Material derivative operator for velocity (used in derived classes).\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Material derivative operator for flux (used in viscoelastic models).\n\nNotes\n-----\n**Viscosity model**: The viscosity tensor :math:`\\boldsymbol{\\eta}` is provided by\nsetting the ``constitutive_model`` property to one of the ``uw.constitutive_models``\nclasses. It may be constant, spatially varying, non-linear, or anisotropic.\n\n**Augmented Lagrangian**: Setting ``penalty`` to a non-zero value adds\n:math:`\\lambda \\nabla \\cdot \\mathbf{u}` to the weak form, improving convergence\nfor incompressible flow (in addition to the constraint equation).\n\n**Mixed finite elements**: The pressure field interpolation order determines\nthe integration order of the mixed method and is typically lower than the\nvelocity field order.\n\n**Viscoelastic models**: For viscoelastic behaviour, the flux term contains\nstress history tracked on a particle swarm. See :class:`SNES_VE_Stokes`.\n\nSee Also\n--------\nSNES_VE_Stokes : Viscoelastic Stokes solver with flux history.\nSNES_NavierStokes : Navier-Stokes solver with inertial terms.\nuw.constitutive_models : Available viscosity models.\n\nExamples\n--------\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(0,0), maxCoords=(1,1), cellSize=0.1)\n>>> stokes = uw.systems.Stokes(mesh, degree=2)\n>>> stokes.constitutive_model = uw.constitutive_models.ViscousFlowModel()\n>>> stokes.constitutive_model.Parameters.viscosity = 1.0\n>>> stokes.bodyforce = [0, -1] # gravity\n>>> stokes.solve()", "harvested_comments": [], "status": "partial", "needs": [ @@ -158,14 +206,32 @@ "name": "SNES_VE_Stokes", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 1119, + "line": 1958, "signature": "class SNES_VE_Stokes", "parameters": [], "returns": null, - "existing_docstring": "Viscoelastic Stokes equation solver.\n\nProvides a discrete representation of the Stokes flow equations with\nincompressibility (or near-incompressibility) constraint and a flux\nhistory term for viscoelastic modelling. Inherits from :class:`SNES_Stokes`.\n\nMomentum equation:\n\n.. math::\n\n -\\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\tau} - p \\mathbf{I}\n \\right]}_{\\mathbf{F}} = \\underbrace{\\mathbf{f}}_{\\mathbf{h}}\n\nContinuity equation:\n\n.. math::\n\n \\underbrace{\\nabla \\cdot \\mathbf{u}}_{\\mathbf{h}_p} = 0\n\nThe flux term is a deviatoric stress :math:`\\boldsymbol{\\tau}` related\nto velocity gradients :math:`\\nabla \\mathbf{u}` through a viscosity\ntensor :math:`\\eta`, plus a volumetric (pressure) part :math:`p`:\n\n.. math::\n\n \\mathbf{F}: \\quad \\boldsymbol{\\tau} = \\frac{\\eta}{2}\n \\left( \\nabla \\mathbf{u} + \\nabla \\mathbf{u}^T \\right)\n\nThe constraint equation :math:`\\mathbf{h}_p = 0` is incompressible flow\nby default but can be set to any function of :math:`\\mathbf{u}` and\n:math:`\\nabla \\cdot \\mathbf{u}`.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nvelocityField : MeshVariable, optional\n Mesh variable for velocity. Created automatically if not provided.\npressureField : MeshVariable, optional\n Mesh variable for pressure. Created automatically if not provided.\ndegree : int, default=2\n Polynomial degree for velocity elements.\norder : int, default=2\n Order parameter (typically same as degree).\np_continuous : bool, default=True\n If False, use discontinuous pressure elements.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator (may be used in child classes).\n\nAttributes\n----------\nu : MeshVariable\n Velocity field unknown :math:`\\mathbf{u}`.\np : MeshVariable\n Pressure field unknown :math:`p`.\nbodyforce : sympy.Expr\n Body force term :math:`\\mathbf{f}`.\npenalty : float\n Augmented Lagrangian penalty parameter :math:`\\lambda`.\nsaddle_preconditioner : sympy.Expr\n Preconditioner for saddle point system (default: :math:`1/\\eta`).\n\nNotes\n-----\n- The viscosity tensor :math:`\\boldsymbol{\\eta}` is set via the\n ``constitutive_model`` property\n- For viscoelastic problems, the flux term contains stress history\n tracked on a particle swarm\n- Augmented Lagrangian approach adds :math:`\\lambda \\nabla \\cdot \\mathbf{u}`\n to penalize incompressibility\n- Pressure element order determines mixed FEM integration order\n\nSee Also\n--------\nSNES_Stokes : Base Stokes solver.\nuw.constitutive_models.ViscoElasticPlasticFlowModel : Constitutive model for VE flow.", + "existing_docstring": "Viscoelastic Stokes solver (backward-compatibility wrapper).\n\n.. deprecated::\n Use ``uw.systems.Stokes`` directly with a\n ``ViscoElasticPlasticFlowModel`` constitutive model. The Stokes\n solver now creates stress history infrastructure automatically\n when the constitutive model is assigned (the lazy-creation\n pathway also reads ``stress_history_ddt_kwargs`` from the model\n \u2014 required for ``integrator='etd'`` to allocate\n ``forcing_star``). VE_Stokes pre-creates the DDt at solver\n ``__init__`` time, before the model exists, so it can't see\n those kwargs and is incompatible with ``integrator='etd'``.\n\nMigration: replace::\n\n stokes = uw.systems.VE_Stokes(mesh, velocityField=v, pressureField=p, order=2)\n stokes.constitutive_model = uw.constitutive_models.ViscoElasticPlasticFlowModel\n\nwith::\n\n stokes = uw.systems.Stokes(mesh, velocityField=v, pressureField=p)\n stokes.constitutive_model = uw.constitutive_models.ViscoElasticPlasticFlowModel(\n stokes.Unknowns, order=2,\n )\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\norder : int, default=2\n BDF time integration order for stress history.\n**kwargs\n All other arguments are passed to :class:`SNES_Stokes`.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "SNES_Stokes_Constrained", + "kind": "class", + "file": "src/underworld3/systems/solvers.py", + "line": 2076, + "signature": "class SNES_Stokes_Constrained", + "parameters": [], + "returns": null, + "existing_docstring": "Stokes solver that enforces :math:`\\mathbf{u}\\cdot\\mathbf{n} = g` on a\nboundary via a Lagrange multiplier living **inside** the saddle-point\nsystem \u2014 a single coupled solve, not an outer loop.\n\nA scalar multiplier field :math:`h` is added as a third DM field and grouped\nwith pressure into a 2-way :math:`\\mathbf{u}\\,|\\,[p,h]` Schur split. The\ncoupled block system is\n\n.. math::\n\n \\begin{bmatrix} A & B^{T} & C^{T} \\\\ B & 0 & 0 \\\\ C & 0 & \\varepsilon M\n \\end{bmatrix}\n \\begin{bmatrix} \\mathbf{u} \\\\ p \\\\ h \\end{bmatrix} =\n \\begin{bmatrix} \\mathbf{f} \\\\ 0 \\\\ g \\end{bmatrix},\n\nwhere :math:`C = \\int_\\Gamma (\\mathbf{n}\\cdot\\mathbf{v})\\,\\psi` couples the\nmultiplier to the boundary normal velocity, and :math:`\\varepsilon M =\n\\varepsilon\\int_\\Omega h\\,\\psi` is an interior screening mass that\nde-singularises the otherwise-empty interior :math:`h` block (it does not\nbias the boundary multiplier). At convergence :math:`h|_\\Gamma = -\\,\n\\mathbf{n}\\cdot\\boldsymbol{\\sigma}\\cdot\\mathbf{n}` is the boundary normal\ntraction = dynamic topography; access it via :meth:`multiplier` /\n:meth:`topography`.\n\nThe constraint is enforced in one coupled solve (no outer iteration). The\naugmented-Lagrangian term conditions the :math:`[p,h]` Schur complement\nwithout biasing the multiplier, and the interior multiplier DOFs are reduced\naway so the solved block is boundary-sized.\n\nRuns in parallel: the interior-multiplier reduction is rank-local section\nsurgery so the global system, velocity solve, and gauge-fixed topography are\npartition-independent (validated in\n``tests/parallel/test_1063_constrained_freeslip_parallel.py``). On an\n**enclosed** problem (a pressure null space is active) the constant pressure\nand constant multiplier are gauge-free, and the solver lands on a\npartition-dependent level for each. To keep the raw **pressure** reproducible\nacross ranks the solver pins the surface-mean pressure automatically (see\n:attr:`auto_pressure_gauge`); the raw **multiplier** keeps its own gauge level,\nso read dynamic topography through :meth:`topography` with ``reference=\"mean\"``\n(gauge-invariant). See\n``docs/developer/design/CONSTRAINED_FREESLIP_MULTIPLIER.md``.\n\nSee Also\n--------\nSNES_Stokes : The unconstrained saddle-point solver this extends.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, @@ -175,11 +241,11 @@ "name": "SNES_Projection", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 1323, + "line": 2554, "signature": "class SNES_Projection", "parameters": [], "returns": null, - "existing_docstring": "Scalar projection solver for mapping functions to mesh variables.\n\nSolves :math:`u = \\tilde{f}` where :math:`\\tilde{f}` is a function that\ncan be evaluated within an element and :math:`u` is a mesh variable with\nassociated shape functions.\n\nTypically used to obtain a continuous representation of a function not\nwell-defined at mesh nodes (e.g., derivatives or flux components). More\nbroadly, it is a projection from one basis to another.\n\nThe projection is implemented by solving:\n\n.. math::\n\n -\\nabla \\cdot \\underbrace{\\left[ \\alpha \\nabla u \\right]}_{\\mathbf{F}}\n - \\underbrace{\\left[ u - \\tilde{f} \\right]}_{\\mathbf{h}} = 0\n\nThe term :math:`\\mathbf{F}` provides optional smoothing regularization.\nSetting :math:`\\alpha = 0` gives a pure L2 projection.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable, optional\n Target mesh variable for the projection.\nscalar_Field : MeshVariable, optional\n Alternative name for the target field.\ndegree : int, default=2\n Polynomial degree for the finite element space.\nsolver_name : str, optional\n Name for the solver instance.\nverbose : bool, default=False\n Enable verbose output.\n\nAttributes\n----------\nuw_function : sympy.Expr\n The function :math:`\\tilde{f}` to project.\nsmoothing : float\n The regularization parameter :math:`\\alpha`.\n\nSee Also\n--------\nSNES_Vector_Projection : Vector field projection.\nSNES_Tensor_Projection : Tensor field projection.", + "existing_docstring": "Scalar projection solver for mapping functions to mesh variables.\n\nSolves :math:`u = \\tilde{f}` where :math:`\\tilde{f}` is a function that\ncan be evaluated within an element and :math:`u` is a mesh variable with\nassociated shape functions.\n\nTypically used to obtain a continuous representation of a function not\nwell-defined at mesh nodes (e.g., derivatives or flux components). More\nbroadly, it is a projection from one basis to another.\n\nStrong form (the screened-Poisson smoother)\n-------------------------------------------\n\nThe projection is implemented by solving\n\n.. math::\n\n u - \\nabla \\cdot \\left( \\alpha \\nabla u \\right) = \\tilde{f},\n\nor equivalently, in the more familiar Helmholtz form\n\n.. math::\n\n u - \\alpha \\, \\nabla^{2} u \\;=\\; \\tilde{f}.\n\nWith :math:`\\alpha = 0` this is a pure pointwise L2 projection of\n:math:`\\tilde f` onto the discrete space of :math:`u`. With\n:math:`\\alpha > 0` it is a *screened-Poisson smoother*: the equation\nenforces a balance between fidelity to :math:`\\tilde f` and curvature of\n:math:`u`. The natural length scale that emerges from this balance is\n\n.. math::\n\n L \\;=\\; \\sqrt{\\alpha},\n\nso :math:`\\alpha` has dimensions of **length squared**. The free-space\nGreen's function of the operator decays as\n:math:`\\exp(-r/L)` (in 2D it is\n:math:`G(r) \\propto K_{0}(r/L)/L^{2}`), so the solution behaves like a\nGaussian-like convolution of :math:`\\tilde f` of width :math:`L` \u2014\nobtained implicitly by one elliptic solve, without ever assembling\nthe kernel. Features in :math:`\\tilde f` of scale much smaller than\n:math:`L` are attenuated; features much larger than :math:`L` pass\nthrough essentially unchanged.\n\nWeak form\n---------\n\nMultiplying by a test function :math:`v` and integrating by parts gives\nthe symmetric weak form actually assembled by PETSc:\n\n.. math::\n\n \\int_\\Omega (u - \\tilde f)\\, v \\; + \\;\n \\int_\\Omega \\alpha \\, \\nabla u \\cdot \\nabla v\n \\;=\\; 0,\n\nwhich is exactly minimising\n:math:`\\tfrac{1}{2}\\!\\int (u-\\tilde f)^2 + \\tfrac{\\alpha}{2}\\!\\int\n|\\nabla u|^2` \u2014 a Tikhonov-regularised L2 projection.\n\nSetting the smoothing length\n----------------------------\n\nTwo equivalent accessors are provided:\n\n* :attr:`smoothing` \u2014 set the coefficient :math:`\\alpha` directly\n (units of length\u00b2). Historically used with tiny values (e.g.\n :math:`10^{-6}`) as a *numerical* regulariser, which corresponds to\n a sub-grid :math:`L` and produces no physical smoothing.\n* :attr:`smoothing_length` \u2014 set :math:`L` directly (length units,\n unit-aware via :func:`underworld3.non_dimensionalise`). This is the\n recommended path when you actually want the projection to act as\n a low-pass filter of a chosen physical scale.\n\nSee Also\n--------\nSNES_Vector_Projection : Vector field projection.\nSNES_Tensor_Projection : Tensor field projection.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable, optional\n Target mesh variable for the projection.\nscalar_Field : MeshVariable, optional\n Alternative name for the target field.\ndegree : int, default=2\n Polynomial degree for the finite element space.\nsolver_name : str, optional\n Name for the solver instance.\nverbose : bool, default=False\n Enable verbose output.", "harvested_comments": [], "status": "partial", "needs": [ @@ -192,11 +258,11 @@ "name": "SNES_Vector_Projection", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 1446, + "line": 2912, "signature": "class SNES_Vector_Projection", "parameters": [], "returns": null, - "existing_docstring": "Vector projection solver for mapping vector functions to mesh variables.\n\nSolves :math:`\\mathbf{u} = \\tilde{\\mathbf{f}}` where :math:`\\tilde{\\mathbf{f}}`\nis a vector function that can be evaluated within an element and\n:math:`\\mathbf{u}` is a vector mesh variable with associated shape functions.\n\nTypically used to obtain a continuous representation of a vector function\nnot well-defined at mesh nodes (e.g., gradient or flux vectors).\n\nThe projection is implemented by solving:\n\n.. math::\n\n -\\nabla \\cdot \\underbrace{\\left[ \\alpha \\nabla \\mathbf{u}\n \\right]}_{\\mathbf{F}} - \\underbrace{\\left[ \\mathbf{u}\n - \\tilde{\\mathbf{f}} \\right]}_{\\mathbf{h}} = 0\n\nThe term :math:`\\mathbf{F}` provides optional smoothing regularization.\nSetting :math:`\\alpha = 0` gives a pure L2 projection.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable, optional\n Target vector mesh variable for the projection.\ndegree : int, default=2\n Polynomial degree for the finite element space.\nverbose : bool, default=False\n Enable verbose output.\n\nAttributes\n----------\nuw_function : sympy.Matrix\n The vector function :math:`\\tilde{\\mathbf{f}}` to project.\nsmoothing : float\n The regularization parameter :math:`\\alpha`.\n\nSee Also\n--------\nSNES_Projection : Scalar field projection.\nSNES_Tensor_Projection : Tensor field projection.", + "existing_docstring": "Vector projection solver for mapping vector functions to mesh variables.\n\nSolves :math:`\\mathbf{u} = \\tilde{\\mathbf{f}}` where\n:math:`\\tilde{\\mathbf{f}}` is a vector function that can be evaluated\nwithin an element and :math:`\\mathbf{u}` is a vector mesh variable\nwith associated shape functions.\n\nTypically used to obtain a continuous representation of a vector\nfunction not well-defined at mesh nodes (e.g., gradient or flux\nvectors), or as a length-scale-aware smoother of an existing vector\nfield.\n\nStrong form (screened-Poisson, vector-valued)\n---------------------------------------------\n\n.. math::\n\n \\mathbf{u} \\;-\\; \\nabla \\cdot \\left( \\alpha\\, \\nabla \\mathbf{u}\n \\right)\n \\;+\\; \\lambda \\left( \\nabla \\cdot \\mathbf{u} \\right) \\mathbf{I}\n \\;=\\; \\tilde{\\mathbf{f}} .\n\nThe :math:`\\alpha`-term is the same screened-Poisson smoother as\nin :class:`SNES_Projection`, applied component-wise: it has the same\n:math:`L = \\sqrt{\\alpha}` smoothing-length interpretation and the\nsame :math:`\\exp(-r/L)` Green's function. The extra :math:`\\lambda`\nterm is a divergence penalty (see :attr:`penalty`) \u2014 set it nonzero\nwhen you want an approximately solenoidal projection of\n:math:`\\tilde{\\mathbf{f}}`.\n\nSetting :math:`\\alpha = 0` (and :math:`\\lambda = 0`) gives a pure\npointwise L2 projection. See :class:`SNES_Projection` for the full\nmathematical context; the relationship between :attr:`smoothing`\n(units length\u00b2) and :attr:`smoothing_length` (units length) is the\nsame as for the scalar projection.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable, optional\n Target vector mesh variable for the projection.\ndegree : int, default=2\n Polynomial degree for the finite element space.\nverbose : bool, default=False\n Enable verbose output.\n\nSee Also\n--------\nSNES_Projection : Scalar field projection (full mathematical detail).\nSNES_Tensor_Projection : Tensor field projection.", "harvested_comments": [], "status": "partial", "needs": [ @@ -209,11 +275,28 @@ "name": "SNES_Tensor_Projection", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 1591, + "line": 3120, "signature": "class SNES_Tensor_Projection", "parameters": [], "returns": null, - "existing_docstring": "Tensor projection solver for mapping tensor functions to mesh variables.\n\nSolves :math:`\\mathbf{u} = \\tilde{\\mathbf{f}}` where :math:`\\tilde{\\mathbf{f}}`\nis a tensor-valued function that can be evaluated within an element and\n:math:`\\mathbf{u}` is a tensor mesh variable with associated shape functions.\n\nTypically used to obtain a continuous representation of a tensor function\nnot well-defined at mesh nodes (e.g., stress or strain tensors).\n\nThe projection is implemented by solving:\n\n.. math::\n\n -\\nabla \\cdot \\underbrace{\\left[ \\alpha \\nabla \\mathbf{u}\n \\right]}_{\\mathbf{F}} - \\underbrace{\\left[ \\mathbf{u}\n - \\tilde{\\mathbf{f}} \\right]}_{\\mathbf{h}} = 0\n\nThe term :math:`\\mathbf{F}` provides optional smoothing regularization.\nSetting :math:`\\alpha = 0` gives a pure L2 projection.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\ntensor_Field : MeshVariable, optional\n Target tensor mesh variable for the projection.\nscalar_Field : MeshVariable, optional\n Scalar work variable used internally.\ndegree : int, default=2\n Polynomial degree for the finite element space.\nverbose : bool, default=False\n Enable verbose output.\n\nNotes\n-----\nCurrently implemented component-wise as there is no native solver\nfor tensor unknowns.\n\nSee Also\n--------\nSNES_Projection : Scalar field projection.\nSNES_Vector_Projection : Vector field projection.", + "existing_docstring": "Tensor projection solver for mapping tensor functions to mesh variables.\n\nSolves :math:`\\mathbf{u} = \\tilde{\\mathbf{f}}` where\n:math:`\\tilde{\\mathbf{f}}` is a tensor-valued function that can be\nevaluated within an element and :math:`\\mathbf{u}` is a tensor mesh\nvariable with associated shape functions.\n\nTypically used to obtain a continuous representation of a tensor\nfunction not well-defined at mesh nodes (e.g., stress or strain\ntensors), with optional length-scale smoothing.\n\nStrong form (screened-Poisson, applied component-wise)\n------------------------------------------------------\n\nInternally the solve is decomposed into scalar sub-problems, one per\ntensor component :math:`u_{ij}`; each sub-problem is\n\n.. math::\n\n u_{ij} \\;-\\; \\nabla \\cdot \\left( \\alpha\\, \\nabla u_{ij}\n \\right) \\;=\\; \\tilde{f}_{ij},\n\nidentical to :class:`SNES_Projection`. The smoothing length is\n:math:`L = \\sqrt{\\alpha}` and the Green's function decays as\n:math:`\\exp(-r/L)`; setting :math:`\\alpha = 0` gives a pure\npointwise L2 projection. See :class:`SNES_Projection` for the full\nderivation, the choice between :attr:`smoothing` (length\u00b2) and\n:attr:`smoothing_length` (length, unit-aware), and guidance on\npicking :math:`L` relative to the cell size.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\ntensor_Field : MeshVariable, optional\n Target tensor mesh variable for the projection.\nscalar_Field : MeshVariable, optional\n Scalar work variable used internally.\ndegree : int, default=2\n Polynomial degree for the finite element space.\nverbose : bool, default=False\n Enable verbose output.\n\nNotes\n-----\nCurrently implemented component-wise as there is no native solver\nfor tensor unknowns. Each component sees the same :math:`\\alpha`,\nso the effective smoothing length :math:`L` is uniform across the\ntensor entries.\n\nSee Also\n--------\nSNES_Projection : Scalar field projection (full mathematical detail).\nSNES_Vector_Projection : Vector field projection.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "SNES_MultiComponent_Projection", + "kind": "class", + "file": "src/underworld3/systems/solvers.py", + "line": 3280, + "signature": "class SNES_MultiComponent_Projection", + "parameters": [], + "returns": null, + "existing_docstring": "Multi-component projection solver.\n\nProjects an N-component row-matrix expression onto an N-component\nmesh variable in a **single** SNES solve, sharing one DM across all\ncomponents. Replaces the per-component cycling used by\n:class:`SNES_Tensor_Projection`, which tears down and rebuilds the\nPETSc DM on every inner iteration.\n\nStrong form (block-diagonal screened Poisson)\n---------------------------------------------\n\nThere is no cross-component coupling, so each component\n:math:`u_k,\\ k=1,\\dots,N_c` satisfies the same scalar\nscreened-Poisson equation as :class:`SNES_Projection`,\n\n.. math::\n\n u_k \\;-\\; \\nabla \\cdot \\left( \\alpha\\, \\nabla u_k \\right)\n \\;=\\; \\tilde f_k.\n\nSetting :math:`\\alpha = 0` gives a pure pointwise L2 projection per\ncomponent. The smoothing length is :math:`L = \\sqrt{\\alpha}` and the\nGreen's function decays as :math:`\\exp(-r/L)` \u2014 the same Gaussian-like\nconvolution interpretation as the scalar case. All components share a\nsingle :math:`\\alpha`, so the smoothing scale :math:`L` is uniform\nacross the multi-component target. Use :attr:`smoothing` to set\n:math:`\\alpha` (units length\u00b2) or :attr:`smoothing_length` for the\nL-valued, unit-aware knob. See :class:`SNES_Projection` for the full\nderivation and guidance.\n\nParameters\n----------\nmesh : Mesh\nu_Field : MeshVariable, optional\n Target ``(1, n_components)`` MATRIX variable. If ``None``, one is\n created.\nn_components : int, optional\n Number of scalar components; required if ``u_Field`` is ``None``.\ndegree : int, default=2\nverbose : bool, default=False\n\nSee Also\n--------\nSNES_Projection : Scalar projection (Nc=1) \u2014 full mathematical detail.\nSNES_Tensor_Projection : Legacy per-component cycling projector.", "harvested_comments": [], "status": "partial", "needs": [ @@ -226,11 +309,11 @@ "name": "SNES_AdvectionDiffusion", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 1739, + "line": 3458, "signature": "class SNES_AdvectionDiffusion", "parameters": [], "returns": null, - "existing_docstring": "Advection-diffusion equation solver using semi-Lagrangian Crank-Nicolson.\n\nImplements the characteristics-based method described in Spiegelman & Katz (2006):\n\n.. math::\n\n \\underbrace{\\frac{\\partial u}{\\partial t} + \\left( \\mathbf{v} \\cdot \\nabla\n \\right) u}_{\\dot{u}} - \\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\kappa}\n \\nabla u \\right]}_{\\mathbf{F}} = \\underbrace{f}_{\\mathbf{h}}\n\nThe flux term :math:`\\mathbf{F}` relates diffusive fluxes to gradients in\n:math:`u`. Advective fluxes along the velocity field :math:`\\mathbf{v}` are\nhandled in the :math:`\\dot{u}` term.\n\nThe time derivative :math:`\\dot{u}` involves upstream sampling to find\n:math:`u^*`, the value of :math:`u` at points which later arrive at mesh\nnodes. This is achieved using a hidden swarm variable advected backwards\nfrom nodal points automatically during solve.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable\n Mesh variable for the transported scalar.\nV_fn : MeshVariable or sympy.Basic\n Velocity field for advection.\norder : int, default=1\n Time integration order (1 or 2).\nrestore_points_func : callable, optional\n Function to restore particles to valid domain.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for the unknown.\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for the flux.\n\nAttributes\n----------\nu : MeshVariable\n The scalar unknown.\nf : sympy.Expr\n Volumetric source term.\n\nNotes\n-----\n- The diffusivity :math:`\\kappa` is set via the ``constitutive_model`` property\n- Sources :math:`f` can be any sympy expression involving mesh/swarm variables\n\nReferences\n----------\nSpiegelman, M., & Katz, R. F. (2006). A semi-Lagrangian Crank-Nicolson\nalgorithm for the numerical solution of advection-diffusion problems.\n*Geochemistry, Geophysics, Geosystems*, 7(4).\nhttps://doi.org/10.1029/2005GC001073\n\nSee Also\n--------\nSNES_Diffusion : Pure diffusion solver without advection.\nSNES_Navier_Stokes : Full momentum advection-diffusion.", + "existing_docstring": "Advection-diffusion equation solver using semi-Lagrangian Crank-Nicolson.\n\nImplements the characteristics-based method described in Spiegelman & Katz (2006):\n\n.. math::\n\n \\underbrace{\\frac{\\partial u}{\\partial t} + \\left( \\mathbf{v} \\cdot \\nabla\n \\right) u}_{\\dot{u}} - \\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\kappa}\n \\nabla u \\right]}_{\\mathbf{F}} = \\underbrace{f}_{\\mathbf{h}}\n\nThe flux term :math:`\\mathbf{F}` relates diffusive fluxes to gradients in\n:math:`u`. Advective fluxes along the velocity field :math:`\\mathbf{v}` are\nhandled in the :math:`\\dot{u}` term.\n\nThe time derivative :math:`\\dot{u}` involves upstream sampling to find\n:math:`u^*`, the value of :math:`u` at points which later arrive at mesh\nnodes. This is achieved using a hidden swarm variable advected backwards\nfrom nodal points automatically during solve.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable\n Mesh variable for the transported scalar.\nV_fn : MeshVariable or sympy.Basic\n Velocity field for advection.\norder : int, default=1\n Time integration order. Note the scheme has **two** order knobs that\n must be paired consistently (see ``theta``):\n\n - the advective time-derivative ``DuDt`` carries the **BDF** order of\n the backward difference along the characteristic;\n - the diffusive flux ``DFDt`` carries the **Adams-Moulton/\u03b8** flux\n integrator.\n\n When ``DuDt`` is built internally (``DuDt=None``) it is fixed at BDF\n order 1, so this ``order`` raises only the *flux* AM order \u2014 i.e.\n ``order=1, theta=0.5`` is the canonical **SLCN** (BDF1 difference +\n Crank-Nicolson flux), the standard second-order scheme. To run\n **SL-BDF2** (BDF2 difference + Backward-Euler-centred flux, second\n order without CN's spurious resonance) supply an explicit order-2\n ``DuDt`` and set ``theta=1.0`` on the flux:\n\n .. code-block:: python\n\n duDt = uw.systems.ddt.SemiLagrangian(\n mesh, T.sym, V_fn, vtype=uw.VarType.SCALAR,\n degree=T.degree, continuous=T.continuous, order=2)\n adv = uw.systems.AdvDiffusionSLCN(mesh, T, V_fn, DuDt=duDt, order=1)\n adv.DFDt.theta = 1.0 # flux implicit at n+1 (BDF2-consistent)\n\n A BDF2 stencil with a Crank-Nicolson flux (``order=2`` + ``theta=0.5``)\n centres the two sides at different times and is **not** a consistent\n second-order scheme. See\n ``docs/advanced/semi-lagrangian-time-integration.md``.\nrestore_points_func : callable, optional\n Function to restore particles to valid domain.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for the unknown.\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for the flux.\nmonotone_mode : str or None, optional\n Monotonicity limiter for the semi-Lagrangian trace-back.\n Forwarded to the internally-constructed ``SemiLagrangian_DDt``\n instances for ``DuDt`` and ``DFDt``.\n\n - ``None`` (default): pure FE trace-back. Can overshoot at\n non-nodal upstream points in cells with sharp gradients\n (e.g. thin boundary layers in deformed cells); legacy\n behaviour.\n - ``\"clamp\"``: clip the FE trace-back result to\n ``[nbr_min, nbr_max]`` of the ``k = dim + 1`` nearest\n ``psi_star`` DOFs. Bit-identical to pure FE in smooth\n regions; bounds overshoots where the FE interpolant would\n otherwise leave the local data range.\n - ``\"pick\"``: keep the FE result where in-bounds; for\n out-of-bounds DOFs only, re-evaluate via Shepard's-method\n RBF. More conservative than clamp at the catastrophe edge.\n\n When a user supplies a pre-built ``DuDt``, this kwarg is\n applied to the internally-constructed ``DFDt`` only \u2014 the\n user's ``DuDt`` already encodes whatever ``monotone_mode``\n it was constructed with.\ntheta : float, default=0.5\n Adams-Moulton theta for the diffusive flux at order 1.\n Forwarded to the internally-constructed\n ``SemiLagrangian_DDt`` instances (same forwarding rule as\n ``monotone_mode``).\n\n - ``0.5`` (default): Crank-Nicolson, A-stable but not\n L-stable. Second-order accurate. Rings on stiff modes\n with sharp gradients (negative amplification factor).\n - ``1.0``: Backward Euler, L-stable, monotone for the\n diffusive flux. First-order accurate. Recommended when\n SLCN+CN ringing dominates the discretisation error.\n - ``0.0``: Forward Euler \u2014 unstable for stiff diffusion;\n included for completeness.\nold_frame_traceback : bool, default=False\n Use the old-frame semi-Lagrangian reach-back for the advective\n ``DuDt`` history on a moving mesh (free surface or interior-node\n adaptation). Forwarded to the internally-constructed ``DuDt``\n only \u2014 the diffusive ``DFDt`` keeps the standard ALE path\n (validated sufficient at Ra=1e5; the unstable mode rides the\n advective scalar, not the dissipative flux).\n\n When ``True``, the trace-back computes the departure foot from\n the PHYSICAL velocity and samples ``psi_star`` on the mesh\n ephemerally restored to the previous-step geometry, instead of\n the lossy ``v_mesh = \u0394x/dt`` fold on the new mesh. This cures the\n high-Ra free-surface convection blow-up (T leaves [0,1] ~step 20\n at Ra=1e5). **Contract:** pass the physical velocity as\n ``V_fn`` (NOT ``v \u2212 v_mesh``) \u2014 the old-frame trace-back must not\n double-compensate the mesh motion. See\n ``docs/developer/design/lagged-clone-sl-history.md``.\n\nNotes\n-----\n- The diffusivity :math:`\\kappa` is set via the ``constitutive_model`` property\n- Sources :math:`f` can be any sympy expression involving mesh/swarm variables\n\nReferences\n----------\nSpiegelman, M., & Katz, R. F. (2006). A semi-Lagrangian Crank-Nicolson\nalgorithm for the numerical solution of advection-diffusion problems.\n*Geochemistry, Geophysics, Geosystems*, 7(4).\nhttps://doi.org/10.1029/2005GC001073\n\nBonaventura, L., Calzola, E., Carlini, E., & Ferretti, R. (2021). Second\norder fully semi-Lagrangian discretizations of advection-diffusion-reaction\nsystems. *Journal of Scientific Computing*, 88, 23.\nhttps://doi.org/10.1007/s10915-021-01518-8 (SL-BDF2 vs SL-CN).\n\nSee Also\n--------\nSNES_Diffusion : Pure diffusion solver without advection.\nSNES_Navier_Stokes : Full momentum advection-diffusion.", "harvested_comments": [], "status": "partial", "needs": [ @@ -243,11 +326,11 @@ "name": "SNES_Diffusion", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 2255, + "line": 4155, "signature": "class SNES_Diffusion", "parameters": [], "returns": null, - "existing_docstring": "Diffusion equation solver using mesh-based finite elements.\n\nSolves the scalar diffusion equation:\n\n.. math::\n\n \\underbrace{\\frac{\\partial u}{\\partial t}}_{\\dot{f}}\n - \\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\kappa} \\nabla u\n \\right]}_{\\mathbf{F}} = \\underbrace{f}_{h}\n\nThe flux term :math:`\\mathbf{F}` relates diffusive fluxes to gradients\nin the unknown :math:`u`.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable\n Mesh variable for the diffusing scalar.\norder : int, default=1\n Time integration order.\ntheta : float, default=0.0\n Time integration parameter (0=explicit, 0.5=Crank-Nicolson, 1=implicit).\nevalf : bool, default=False\n Numerically evaluate symbolic expressions during setup.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : Eulerian_DDt, SemiLagrangian_DDt, or Lagrangian_DDt, optional\n Time derivative operator for the unknown.\nDFDt : Eulerian_DDt, SemiLagrangian_DDt, or Lagrangian_DDt, optional\n Time derivative operator for the flux.\n\nAttributes\n----------\nu : MeshVariable\n The scalar unknown.\nf : sympy.Expr\n Volumetric source term.\n\nNotes\n-----\n- The diffusivity :math:`\\kappa` is set via the ``constitutive_model`` property\n- Sources :math:`f` can be any sympy expression involving mesh/swarm variables\n\nSee Also\n--------\nSNES_AdvectionDiffusion : Adds advection transport.\nSNES_Poisson : Steady-state diffusion (no time derivative).", + "existing_docstring": "Diffusion equation solver using mesh-based finite elements.\n\nSolves the scalar diffusion equation:\n\n.. math::\n\n \\underbrace{\\frac{\\partial u}{\\partial t}}_{\\dot{f}}\n - \\nabla \\cdot \\underbrace{\\left[ \\boldsymbol{\\kappa} \\nabla u\n \\right]}_{\\mathbf{F}} = \\underbrace{f}_{h}\n\nThe flux term :math:`\\mathbf{F}` relates diffusive fluxes to gradients\nin the unknown :math:`u`.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nu_Field : MeshVariable\n Mesh variable for the diffusing scalar.\norder : int, default=1\n Time integration order.\ntheta : float, default=0.0\n Time integration parameter (0=explicit, 0.5=Crank-Nicolson, 1=implicit).\nevalf : bool, default=False\n Numerically evaluate symbolic expressions during setup.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : Eulerian_DDt, SemiLagrangian_DDt, or Lagrangian_DDt, optional\n Time derivative operator for the unknown.\nDFDt : Eulerian_DDt, SemiLagrangian_DDt, or Lagrangian_DDt, optional\n Time derivative operator for the flux.\n\nNotes\n-----\n- The diffusivity :math:`\\kappa` is set via the ``constitutive_model`` property\n- Sources :math:`f` can be any sympy expression involving mesh/swarm variables\n\nSee Also\n--------\nSNES_AdvectionDiffusion : Adds advection transport.\nSNES_Poisson : Steady-state diffusion (no time derivative).", "harvested_comments": [], "status": "partial", "needs": [ @@ -260,11 +343,11 @@ "name": "SNES_NavierStokes", "kind": "class", "file": "src/underworld3/systems/solvers.py", - "line": 2635, + "line": 4544, "signature": "class SNES_NavierStokes", "parameters": [], "returns": null, - "existing_docstring": "Navier-Stokes equation solver with momentum advection.\n\nProvides a solver for the Navier-Stokes (vector advection-diffusion) equation\nsimilar to the Semi-Lagrange Crank-Nicolson method (Spiegelman & Katz, 2006)\nbut using distributed upstream sampling from a swarm variable.\n\n.. math::\n\n \\underbrace{\\frac{\\partial \\mathbf{u}}{\\partial t}\n + \\left( \\mathbf{u} \\cdot \\nabla \\right) \\mathbf{u}}_{\\dot{\\mathbf{u}}}\n - \\nabla \\cdot \\underbrace{\\left[ \\frac{\\boldsymbol{\\eta}}{2}\n \\left( \\nabla \\mathbf{u} + \\nabla \\mathbf{u}^T \\right)\n - p \\mathbf{I} \\right]}_{\\mathbf{F}} = \\underbrace{\\mathbf{f}}_{\\mathbf{h}}\n\nThe flux term :math:`\\mathbf{F}` relates viscous stresses to velocity gradients.\nAdvective momentum transport is handled in the :math:`\\dot{\\mathbf{u}}` term.\n\nThe time derivative :math:`\\dot{\\mathbf{u}}` involves upstream sampling to find\n:math:`\\mathbf{u}^*`, representing velocity at the start of the timestep. This is\nachieved using a swarm variable that carries history information along flow paths.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nvelocityField : MeshVariable\n Mesh variable for velocity.\npressureField : MeshVariable\n Mesh variable for pressure.\nrho : float or sympy.Expr\n Fluid density.\norder : int, default=1\n Time integration order.\ntheta : float, default=0.5\n Time integration parameter.\np_continuous : bool, default=True\n If False, use discontinuous pressure elements.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for velocity.\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for stress.\n\nAttributes\n----------\nu : MeshVariable\n Velocity field unknown.\np : MeshVariable\n Pressure field unknown.\nrho : sympy.Expr\n Fluid density.\nbodyforce : sympy.Expr\n Body force term :math:`\\mathbf{f}`.\n\nNotes\n-----\n- The viscosity :math:`\\eta` is set via the ``constitutive_model`` property\n- High-order shape functions (cubic or higher) are recommended for accurate\n history term interpolation\n- The user must supply and update the swarm variable representing :math:`\\mathbf{u}^*`\n\nReferences\n----------\nSpiegelman, M., & Katz, R. F. (2006). A semi-Lagrangian Crank-Nicolson\nalgorithm for the numerical solution of advection-diffusion problems.\n*Geochemistry, Geophysics, Geosystems*, 7(4).\nhttps://doi.org/10.1029/2005GC001073\n\nSee Also\n--------\nSNES_Stokes : Steady-state Stokes flow (no inertia).\nSNES_AdvectionDiffusion : Scalar advection-diffusion.", + "existing_docstring": "Navier-Stokes equation solver with momentum advection.\n\nProvides a solver for the Navier-Stokes (vector advection-diffusion) equation\nsimilar to the Semi-Lagrange Crank-Nicolson method (Spiegelman & Katz, 2006)\nbut using distributed upstream sampling from a swarm variable.\n\n.. math::\n\n \\underbrace{\\frac{\\partial \\mathbf{u}}{\\partial t}\n + \\left( \\mathbf{u} \\cdot \\nabla \\right) \\mathbf{u}}_{\\dot{\\mathbf{u}}}\n - \\nabla \\cdot \\underbrace{\\left[ \\frac{\\boldsymbol{\\eta}}{2}\n \\left( \\nabla \\mathbf{u} + \\nabla \\mathbf{u}^T \\right)\n - p \\mathbf{I} \\right]}_{\\mathbf{F}} = \\underbrace{\\mathbf{f}}_{\\mathbf{h}}\n\nThe flux term :math:`\\mathbf{F}` relates viscous stresses to velocity gradients.\nAdvective momentum transport is handled in the :math:`\\dot{\\mathbf{u}}` term.\n\nThe time derivative :math:`\\dot{\\mathbf{u}}` involves upstream sampling to find\n:math:`\\mathbf{u}^*`, representing velocity at the start of the timestep. This is\nachieved using a swarm variable that carries history information along flow paths.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nvelocityField : MeshVariable\n Mesh variable for velocity.\npressureField : MeshVariable\n Mesh variable for pressure.\nrho : float or sympy.Expr\n Fluid density.\norder : int, default=1\n Time integration order.\ntheta : float, default=0.5\n Time integration parameter.\np_continuous : bool, default=True\n If False, use discontinuous pressure elements.\nverbose : bool, default=False\n Enable verbose output.\nDuDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for velocity.\nDFDt : SemiLagrangian_DDt or Lagrangian_DDt, optional\n Time derivative operator for stress.\n\nNotes\n-----\n- The viscosity :math:`\\eta` is set via the ``constitutive_model`` property\n- High-order shape functions (cubic or higher) are recommended for accurate\n history term interpolation\n- The user must supply and update the swarm variable representing :math:`\\mathbf{u}^*`\n\nReferences\n----------\nSpiegelman, M., & Katz, R. F. (2006). A semi-Lagrangian Crank-Nicolson\nalgorithm for the numerical solution of advection-diffusion problems.\n*Geochemistry, Geophysics, Geosystems*, 7(4).\nhttps://doi.org/10.1029/2005GC001073\n\nSee Also\n--------\nSNES_Stokes : Steady-state Stokes flow (no inertia).\nSNES_AdvectionDiffusion : Scalar advection-diffusion.", "harvested_comments": [], "status": "partial", "needs": [ @@ -274,11 +357,11 @@ "is_public": true }, { - "name": "KDTree", + "name": "PtrContainer", "kind": "class", - "file": "src/underworld3/ckdtree.pyx", - "line": 17, - "signature": "cdef class KDTree:", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 3, + "signature": "cdef class PtrContainer:", "parameters": [], "returns": null, "existing_docstring": null, @@ -292,11 +375,11 @@ "is_public": true }, { - "name": "Integral", + "name": "AnalyticSolNL_base", "kind": "class", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 16, - "signature": "class Integral:", + "file": "src/underworld3/function/analytic.pyx", + "line": 52, + "signature": "class AnalyticSolNL_base(sympy_function_printable):", "parameters": [], "returns": null, "existing_docstring": null, @@ -310,11 +393,11 @@ "is_public": true }, { - "name": "CellWiseIntegral", + "name": "AnalyticSolNL_velocity_x", "kind": "class", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 208, - "signature": "class CellWiseIntegral:", + "file": "src/underworld3/function/analytic.pyx", + "line": 56, + "signature": "class AnalyticSolNL_velocity_x(AnalyticSolNL_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -328,11 +411,11 @@ "is_public": true }, { - "name": "PtrContainer", + "name": "AnalyticSolNL_velocity_y", "kind": "class", - "file": "src/underworld3/cython/petsc_types.pyx", - "line": 1, - "signature": "cdef class PtrContainer:", + "file": "src/underworld3/function/analytic.pyx", + "line": 61, + "signature": "class AnalyticSolNL_velocity_y(AnalyticSolNL_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -346,11 +429,11 @@ "is_public": true }, { - "name": "CachedDMInterpolationInfo", + "name": "AnalyticSolNL_velocity", "kind": "class", - "file": "src/underworld3/function/_dminterp_wrapper.pyx", - "line": 39, - "signature": "cdef class CachedDMInterpolationInfo:", + "file": "src/underworld3/function/analytic.pyx", + "line": 66, + "signature": "class AnalyticSolNL_velocity(AnalyticSolNL_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -364,11 +447,11 @@ "is_public": true }, { - "name": "UnderworldAppliedFunction", + "name": "AnalyticSolNL_bodyforce_x", "kind": "class", - "file": "src/underworld3/function/_function.pyx", - "line": 50, - "signature": "class UnderworldAppliedFunction(sympy.core.function.AppliedUndef):", + "file": "src/underworld3/function/analytic.pyx", + "line": 74, + "signature": "class AnalyticSolNL_bodyforce_x(AnalyticSolNL_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -382,11 +465,29 @@ "is_public": true }, { - "name": "UnderworldAppliedFunctionDeriv", + "name": "AnalyticSolNL_bodyforce_y", "kind": "class", - "file": "src/underworld3/function/_function.pyx", + "file": "src/underworld3/function/analytic.pyx", + "line": 79, + "signature": "class AnalyticSolNL_bodyforce_y(AnalyticSolNL_base):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "AnalyticSolNL_bodyforce", + "kind": "class", + "file": "src/underworld3/function/analytic.pyx", "line": 84, - "signature": "class UnderworldAppliedFunctionDeriv(UnderworldAppliedFunction):", + "signature": "class AnalyticSolNL_bodyforce(AnalyticSolNL_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -400,11 +501,11 @@ "is_public": true }, { - "name": "UnderworldFunction", + "name": "AnalyticSolNL_viscosity", "kind": "class", - "file": "src/underworld3/function/_function.pyx", + "file": "src/underworld3/function/analytic.pyx", "line": 92, - "signature": "class UnderworldFunction(sympy.Function):", + "signature": "class AnalyticSolNL_viscosity(AnalyticSolNL_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -418,11 +519,11 @@ "is_public": true }, { - "name": "sympy_function_printable", + "name": "AnalyticSolCx_base", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 18, - "signature": "class sympy_function_printable(sympy.Function):", + "line": 105, + "signature": "class AnalyticSolCx_base(sympy_function_printable):", "parameters": [], "returns": null, "existing_docstring": null, @@ -436,11 +537,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_base", + "name": "AnalyticSolCx_velocity_x", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 40, - "signature": "class AnalyticSolNL_base(sympy_function_printable):", + "line": 109, + "signature": "class AnalyticSolCx_velocity_x(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -454,11 +555,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_velocity_x", + "name": "AnalyticSolCx_velocity_y", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 44, - "signature": "class AnalyticSolNL_velocity_x(AnalyticSolNL_base):", + "line": 114, + "signature": "class AnalyticSolCx_velocity_y(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -472,11 +573,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_velocity_y", + "name": "AnalyticSolCx_velocity", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 50, - "signature": "class AnalyticSolNL_velocity_y(AnalyticSolNL_base):", + "line": 119, + "signature": "class AnalyticSolCx_velocity(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -490,11 +591,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_velocity", + "name": "AnalyticSolCx_pressure", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 55, - "signature": "class AnalyticSolNL_velocity(AnalyticSolNL_base):", + "line": 127, + "signature": "class AnalyticSolCx_pressure(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -508,11 +609,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_bodyforce_x", + "name": "AnalyticSolCx_viscosity", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 62, - "signature": "class AnalyticSolNL_bodyforce_x(AnalyticSolNL_base):", + "line": 133, + "signature": "class AnalyticSolCx_viscosity(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -526,11 +627,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_bodyforce_y", + "name": "AnalyticSolCx_stress_xx", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 68, - "signature": "class AnalyticSolNL_bodyforce_y(AnalyticSolNL_base):", + "line": 142, + "signature": "class AnalyticSolCx_stress_xx(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -544,11 +645,11 @@ "is_public": true }, { - "name": "AnalyticSolNL_bodyforce", + "name": "AnalyticSolCx_stress_yy", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 73, - "signature": "class AnalyticSolNL_bodyforce(AnalyticSolNL_base):", + "line": 147, + "signature": "class AnalyticSolCx_stress_yy(AnalyticSolCx_base):", "parameters": [], "returns": null, "existing_docstring": null, @@ -562,11 +663,119 @@ "is_public": true }, { - "name": "AnalyticSolNL_viscosity", + "name": "AnalyticSolCx_stress_xy", "kind": "class", "file": "src/underworld3/function/analytic.pyx", - "line": 80, - "signature": "class AnalyticSolNL_viscosity(AnalyticSolNL_base):", + "line": 152, + "signature": "class AnalyticSolCx_stress_xy(AnalyticSolCx_base):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "TransformedDict", + "kind": "class", + "file": "src/underworld3/scaling/_utils.py", + "line": 24, + "signature": "class TransformedDict", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "class_or_instance_method", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 22, + "signature": "class class_or_instance_method", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "ReadError", + "kind": "class", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 14, + "signature": "class ReadError", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "WriteError", + "kind": "class", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 18, + "signature": "class WriteError", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "CorruptionError", + "kind": "class", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 22, + "signature": "class CorruptionError", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "Xdmf", + "kind": "class", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 8, + "signature": "class Xdmf", "parameters": [], "returns": null, "existing_docstring": null, @@ -598,65 +807,68 @@ "is_public": true }, { - "name": "Constitutive_Model", + "name": "CheckpointBackend", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 122, - "signature": "class Constitutive_Model", + "file": "src/underworld3/checkpoint/backend.py", + "line": 18, + "signature": "class CheckpointBackend", "parameters": [], "returns": null, - "existing_docstring": "Base class for constitutive laws relating gradients to fluxes.\n\nConstitutive laws relate gradients in the unknowns to fluxes of quantities\n(for example, heat fluxes are related to temperature gradients through a\nthermal conductivity). This class is a base class for building Underworld\nconstitutive laws.\n\nIn a scalar problem, the relationship is:\n\n.. math::\n\n q_i = k_{ij} \\frac{\\partial T}{\\partial x_j}\n\nand the constitutive parameters describe :math:`k_{ij}`. The template\nassumes :math:`k_{ij} = \\delta_{ij}`.\n\nIn a vector problem (such as the Stokes problem), the relationship is:\n\n.. math::\n\n t_{ij} = c_{ijkl} \\frac{\\partial u_k}{\\partial x_l}\n\nbut is usually written to eliminate the anti-symmetric part of the\ndisplacement or velocity gradients:\n\n.. math::\n\n t_{ij} = c_{ijkl} \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nand the constitutive parameters describe :math:`c_{ijkl}`. The template\nassumes :math:`k_{ij} = \\frac{1}{2}(\\delta_{ik}\\delta_{jl} + \\delta_{il}\\delta_{jk})`\nwhich is the 4th rank identity tensor accounting for symmetry in the flux\nand the gradient terms.", + "existing_docstring": "Backing-store interface for :class:`underworld3.checkpoint.Snapshot`.\n\nImplementations\n---------------\n- :class:`InMemoryBackend` \u2014 v1; numpy arrays held in process memory.\n- ``OnDiskFullStateBackend`` \u2014 v1.1; single monolithic HDF5 file.\n\nVectors are bulk numerical data; metadata is small scalars / dicts /\nlists describing structure and provenance.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "ViscousFlowModel", + "name": "InMemoryBackend", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 494, - "signature": "class ViscousFlowModel", + "file": "src/underworld3/checkpoint/backend.py", + "line": 43, + "signature": "class InMemoryBackend", "parameters": [], "returns": null, - "existing_docstring": "Viscous flow constitutive model for Stokes-type solvers.\n\nDefines the relationship between deviatoric stress and strain rate:\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity, which can be a scalar constant, SymPy\nfunction, Underworld mesh variable, or any valid combination. This results\nin an isotropic (but not necessarily homogeneous or linear) relationship\nbetween :math:`\\tau` and the velocity gradients.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (typically velocity and pressure fields).\nmaterial_name : str, optional\n Name identifier for this material (used in multi-material setups).\n\nAttributes\n----------\nParameters : _Parameters\n Material parameters container. Set ``Parameters.shear_viscosity_0``\n to define the viscosity.\nflux : sympy.Matrix\n The computed deviatoric stress tensor :math:`\\boldsymbol{\\tau}`.\nC : sympy.Matrix\n Mandel form of the constitutive tensor :math:`\\eta_{IJ}`.\nc : sympy.Array\n Rank-4 tensor form :math:`\\eta_{ijkl}`.\n\nExamples\n--------\n>>> import underworld3 as uw\n>>> stokes = uw.systems.Stokes(mesh)\n>>> viscous = uw.constitutive_models.ViscousFlowModel(stokes.Unknowns)\n>>> viscous.Parameters.shear_viscosity_0 = 1e21 # Pa.s\n>>> stokes.constitutive_model = viscous\n\nSee Also\n--------\nViscoPlasticFlowModel : Adds yield stress for plastic behavior.\nViscoElasticPlasticFlowModel : Adds viscoelastic memory.", + "existing_docstring": "Snapshot storage in process memory.\n\nEager-copy on both ``save_vector`` and ``load_vector`` per the v1\nscope-boundary (no lazy / copy-on-write semantics). Per-snapshot\nbyte cost is the sum of captured vector sizes \u2014 expected to be\nbounded by one Stokes solve's working memory for typical setups.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "ViscoPlasticFlowModel", + "name": "SnapshotInvalidatedError", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 725, - "signature": "class ViscoPlasticFlowModel", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 52, + "signature": "class SnapshotInvalidatedError", "parameters": [], "returns": null, - "existing_docstring": "Viscoplastic flow constitutive model with yield stress.\n\nExtends :class:`ViscousFlowModel` with a yield stress that limits the\nmaximum deviatoric stress. When stress would exceed the yield stress,\nthe effective viscosity is reduced to cap the stress.\n\n.. math::\n\n \\tau_{ij} = \\eta_\\mathrm{eff} \\cdot \\dot{\\varepsilon}_{ij}\n\nwhere the effective viscosity is:\n\n.. math::\n\n \\eta_\\mathrm{eff} = \\min\\left(\\eta_0, \\frac{\\tau_y}{2\\dot{\\varepsilon}_{II}}\\right)\n\nand :math:`\\tau_y` is the yield stress and :math:`\\dot{\\varepsilon}_{II}`\nis the second invariant of the strain rate.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (typically velocity and pressure fields).\nmaterial_name : str, optional\n Name identifier for this material.\n\nAttributes\n----------\nParameters : _Parameters\n Material parameters including:\n\n - ``shear_viscosity_0``: Background viscosity :math:`\\eta_0`\n - ``yield_stress``: Yield stress :math:`\\tau_y`\n\nviscosity : UWexpression\n The effective (possibly yielded) viscosity.\n\nNotes\n-----\nIf yield stress is not defined, this model behaves identically to\n:class:`ViscousFlowModel`. The message ``not~yet~defined`` in the\neffective viscosity indicates missing parameters.\n\nSee Also\n--------\nViscousFlowModel : Base viscous model without yielding.\nViscoElasticPlasticFlowModel : Adds viscoelastic memory.", + "existing_docstring": "Raised when a snapshot can no longer be restored faithfully.\n\nTriggers in v1:\n\n- A captured mesh / swarm / variable name is no longer present on\n the target :class:`underworld3.Model`.\n- Mesh ``_mesh_version`` differs from the snapshot's captured\n value. v1 treats this as fatal because the captured DOF arrays\n are sized for the pre-adapt section. **v1.2 will replace this\n refusal with a mesh-rebuild path** on the same principle as the\n swarm rebuild \u2014 see the design note's mesh-adapt scope section.\n\nNotably **not** a trigger: swarm population mutation\n(populate / migrate / add_particles / remesh) between capture and\nrestore. The swarm restore path *rebuilds* the local particle\npopulation from the snapshot, so intervening mutations are exactly\nwhat restore is for. The ``_population_generation`` counter on the\nswarm is informational, not a restore gate.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "ViscoElasticPlasticFlowModel", + "name": "Snapshot", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 945, - "signature": "class ViscoElasticPlasticFlowModel", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 80, + "signature": "class Snapshot", "parameters": [], "returns": null, - "existing_docstring": "Viscoelastic-plastic flow constitutive model.\n\nThe stress (flux term) is given by:\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity, a scalar constant, SymPy function,\nUnderworld mesh variable, or any valid combination. This results in an\nisotropic (but not necessarily homogeneous or linear) relationship between\n:math:`\\tau` and the velocity gradients. You can also supply :math:`\\eta_{IJ}`,\nthe Mandel form of the constitutive tensor, or :math:`\\eta_{ijkl}`, the rank-4 tensor.\n\nThe Mandel constitutive matrix is available in `viscous_model.C` and the rank 4 tensor form is\nin `viscous_model.c`. Apply the constitutive model using:", + "existing_docstring": "Unitary state token.\n\nProduced by :func:`snapshot`; consumed by :func:`restore`. Holds a\nbackend (where the bulk arrays live) plus per-model bookkeeping \u2014\nwhich meshes and swarms were captured, in what order, with what\nvariable sets.\n\nAttributes\n----------\nbackend\n Where the captured arrays and small metadata live.\nschema_version\n Snapshot file-format version. Restore refuses on mismatch in\n v1; v1.1's migration registry will lift older versions to the\n current schema for on-disk restore only.\nmesh_names\n Capture order of mesh names. ``mesh.name`` is the stable key.\nmesh_versions\n Per-mesh ``_mesh_version`` at the moment of capture. v1\n compares strictly; v1.2 will rebuild on mismatch.\nmeshvar_names\n Mapping ``mesh_name \u2192 [var clean_name, ...]``.\nswarm_names\n Capture order of swarm stable names\n (``f\"swarm_{instance_number}\"``).\nswarm_mesh_names\n Mapping ``swarm_name \u2192 mesh_name`` so restore can verify the\n swarm's parent mesh is still the captured one.\nswarm_generations\n Captured ``_population_generation`` per swarm \u2014 informational\n metadata; *not* a restore gate. Useful for logs and debugging\n (\"this snapshot was taken at generation 7; the current swarm\n is at 12\").\nswarmvar_names\n Mapping ``swarm_name \u2192 [user-var clean_name, ...]``. Internal\n DMSwarm-prefixed variables are filtered out.\nmetadata\n Free-form user/system metadata (simulation time, step counter,\n ...). Not load-bearing for restore correctness.", "harvested_comments": [], "status": "partial", "needs": [ @@ -667,31 +879,32 @@ "is_public": true }, { - "name": "DiffusionModel", + "name": "SnapshottableState", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1537, - "signature": "class DiffusionModel", + "file": "src/underworld3/checkpoint/state.py", + "line": 39, + "signature": "class SnapshottableState", "parameters": [], "returns": null, - "existing_docstring": "Diffusion (Fourier/Fick) constitutive model for scalar transport.\n\nDefines the flux-gradient relationship for scalar diffusion:\n\n.. math::\n\n q_{i} = \\kappa_{ij} \\frac{\\partial \\phi}{\\partial x_j}\n\nFor isotropic diffusion, :math:`\\kappa_{ij} = \\kappa \\delta_{ij}`.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (the scalar field being diffused).\nmaterial_name : str, optional\n Name identifier for this material.\n\nAttributes\n----------\nParameters : _Parameters\n Material parameters container. Set ``Parameters.diffusivity``\n to define :math:`\\kappa`.\nflux : sympy.Matrix\n The computed diffusive flux vector.\ndiffusivity : UWexpression\n Shortcut to ``Parameters.diffusivity``.\nK : UWexpression\n Alias for ``diffusivity``.\n\nExamples\n--------\n>>> diffusion = uw.constitutive_models.DiffusionModel(poisson.Unknowns)\n>>> diffusion.Parameters.diffusivity = 1e-6 # m^2/s\n>>> poisson.constitutive_model = diffusion\n\nSee Also\n--------\nAnisotropicDiffusionModel : For direction-dependent diffusivity.", + "existing_docstring": "Base for every per-class State dataclass.\n\nSubclasses add their own fields. The single mandatory field is\n``_schema_version`` \u2014 an integer that v1.1's on-disk migration\nregistry uses to lift older snapshots to the current schema. In\nv1 (in-memory only) the version is checked for strict equality;\nany mismatch is a programming error since capture and restore\nhappen in the same process.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "AnisotropicDiffusionModel", + "name": "Snapshottable", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1659, - "signature": "class AnisotropicDiffusionModel", + "file": "src/underworld3/checkpoint/state.py", + "line": 54, + "signature": "class Snapshottable", "parameters": [], "returns": null, - "existing_docstring": "Anisotropic diffusion with direction-dependent diffusivities.\n\nDefines a diagonal diffusivity tensor :math:`\\kappa_{ij} = \\text{diag}(\\kappa_0, \\kappa_1, ...)`\nfor direction-dependent diffusion rates.", + "existing_docstring": "Structural protocol for state-bearing objects.\n\nAn object is Snapshottable if::\n\n obj.state # readable\n obj.state = obj.state # writable\n isinstance(obj.state, SnapshottableState)\n\nThe snapshot mechanism uses :attr:`state` to capture and restore.\nImplementations choose between:\n\n- **Option (C), authoritative dataclass.** ``state`` is a stored\n attribute holding the dataclass; every mutation site on the class\n writes ``self.state.`` directly. Best for new code.\n\n- **Option (B), derived dataclass.** ``state`` is a property that\n builds the dataclass from existing private attrs on each read,\n and the setter writes attrs back. Best for retrofits of existing\n classes (DDt, ParameterRegistry).", "harvested_comments": [], "status": "partial", "needs": [ @@ -702,14 +915,14 @@ "is_public": true }, { - "name": "GenericFluxModel", + "name": "TrackerState", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1727, - "signature": "class GenericFluxModel", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 41, + "signature": "class TrackerState", "parameters": [], "returns": null, - "existing_docstring": "A generic constitutive model with symbolic flux expression.\n\nExample usage:\n```python\ngrad_phi = sympy.Matrix([sp.Symbol(\"\u2202\u03c6/\u2202x\"), sp.Symbol(\"\u2202\u03c6/\u2202y\")])\nflux_expr = sympy.Matrix([[kappa_11, kappa_12], [kappa_21, kappa_22]]) * grad_phi\n\nmodel = GenericFluxModel(dim=2)\nmodel.flux = flux_expr\nscalar_solver.constititutive_model = model\n```", + "existing_docstring": "Snapshot of a :class:`ModelTracker`.\n\nThe tracker is extensible, so the State carries an open mapping\nrather than fixed fields. ``time`` / ``step`` / ``dt`` are\nordinary entries in ``managed``.", "harvested_comments": [], "status": "partial", "needs": [ @@ -720,31 +933,32 @@ "is_public": true }, { - "name": "DarcyFlowModel", + "name": "ModelTracker", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1804, - "signature": "class DarcyFlowModel", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 53, + "signature": "class ModelTracker", "parameters": [], "returns": null, - "existing_docstring": "Darcy flow constitutive model for porous media flow.\n\nRelates the Darcy flux to pressure gradients and body forces:\n\n.. math::\n\n q_{i} = \\kappa_{ij} \\left( \\frac{\\partial p}{\\partial x_j} - s_j \\right)\n\nwhere :math:`\\kappa` is the permeability (or hydraulic conductivity),\n:math:`p` is the pressure (or hydraulic head), and :math:`s` is the\nbody force term (e.g., gravity: :math:`s = \\rho g`).\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (the pressure/head field).\nmaterial_name : str, optional\n Name identifier for this material.\n\nAttributes\n----------\nParameters : _Parameters\n Material parameters container:\n\n - ``permeability``: Intrinsic permeability :math:`\\kappa` [m\u00b2]\n - ``s``: Body force vector (e.g., gravity term)\n\nflux : sympy.Matrix\n The computed Darcy flux vector.\npermeability : UWexpression\n Shortcut to ``Parameters.permeability``.\n\nExamples\n--------\n>>> darcy = uw.constitutive_models.DarcyFlowModel(solver.Unknowns)\n>>> darcy.Parameters.permeability = 1e-12 # m^2\n>>> darcy.Parameters.s = [0, -rho * g] # Gravity in y-direction\n>>> solver.constitutive_model = darcy\n\nSee Also\n--------\nDiffusionModel : For pure diffusion without body forces.", + "existing_docstring": "One per :class:`underworld3.Model`, auto-registered as a\n:class:`~underworld3.checkpoint.Snapshottable` state-bearer so the\nsnapshot machinery captures and restores it with no extra\nplumbing. See the module docstring for the user-facing contract.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "TransverseIsotropicFlowModel", + "name": "KDTree", "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1958, - "signature": "class TransverseIsotropicFlowModel", + "file": "src/underworld3/ckdtree.pyx", + "line": 37, + "signature": "cdef class KDTree:", "parameters": [], "returns": null, - "existing_docstring": "Transversely isotropic (anisotropic) viscous flow model.\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity tensor defined as:\n\n.. math::\n\n \\eta_{ijkl} = \\eta_0 \\cdot I_{ijkl} + (\\eta_0-\\eta_1) \\left[ \\frac{1}{2} \\left[\n n_i n_l \\delta_{jk} + n_j n_k \\delta_{il} + n_i n_l \\delta_{jk}\n + n_j n_l \\delta_{ik} \\right] - 2 n_i n_j n_k n_l \\right]\n\nand :math:`\\hat{\\mathbf{n}} \\equiv \\{n_i\\}` is the unit vector defining\nthe local orientation of the weak plane (a.k.a. the director).\n\nThe Mandel constitutive matrix is available in ``viscous_model.C`` and the\nrank-4 tensor form is in ``viscous_model.c``.\n\nExamples\n--------\n>>> viscous_model = TransverseIsotropicFlowModel(dim)\n>>> viscous_model.material_properties = viscous_model.Parameters(\n... eta_0=viscosity_fn,\n... eta_1=weak_viscosity_fn,\n... director=orientation_vector_fn\n... )\n>>> solver.constitutive_model = viscous_model\n>>> tau = viscous_model.flux(gradient_matrix)\n---", + "existing_docstring": "\n Unit-aware KD-Tree for spatial indexing and queries.\n\n This class generates a kd-tree index for the provided points and provides\n the necessary methods for finding which points are closest to a given query\n location. It automatically handles coordinate units when provided.\n\n This class utilises `nanoflann` for kd-tree functionality.\n\n .. note::\n The vendored ``nanoflann.hpp`` is version **1.3.2** (2021).\n Upstream nanoflann is at **1.9.0** as of 2026-02. Consider updating\n for ~20% performance improvement on small point clouds and accumulated\n bug fixes. See https://github.com/jlblancoc/nanoflann/releases\n See planning file: underworld.md (Nice to Have, 2026-02-13)\n\n Parameters\n ----------\n points : array-like\n The points for which the kd-tree index will be built. This\n should be a 2-dimensional array of size (n_points, dim).\n Can be unit-aware (UnitAwareArray) or plain numpy array.\n\n Example\n -------\n >>> import numpy as np\n >>> import underworld3 as uw\n\n Generate a random set of points\n >>> pts = np.random.random( size=(100,2) )\n\n Build the index on the points\n >>> index = uw.kdtree.KDTree(pts)\n\n Search the index for a coordinate\n >>> coord = np.zeros((1,2))\n >>> coord[0] = (0.5,0.5)\n >>> indices, dist_sqr, found = index.find_closest_point(coord)\n\n Confirm that a point has been found\n >>> found[0]\n True\n\n", "harvested_comments": [], "status": "partial", "needs": [ @@ -754,32 +968,31 @@ "is_public": true }, { - "name": "MultiMaterialConstitutiveModel", + "name": "Constitutive_Model", "kind": "class", "file": "src/underworld3/constitutive_models.py", - "line": 2158, - "signature": "class MultiMaterialConstitutiveModel", + "line": 229, + "signature": "class Constitutive_Model", "parameters": [], "returns": null, - "existing_docstring": "Multi-material constitutive model using level-set weighted flux averaging.\n\nMathematical Foundation:\n\n.. math::\n\n \\mathbf{f}_{\\text{composite}}(\\mathbf{x}) = \\sum_{i=1}^{N}\n \\phi_i(\\mathbf{x}) \\cdot \\mathbf{f}_i(\\mathbf{x})\n\nCritical Architecture:\n\n- Solver owns Unknowns (including :math:`D\\mathbf{F}/Dt` stress history)\n- All constituent models share solver's Unknowns\n- Composite flux becomes stress history for all materials", + "existing_docstring": "Base class for constitutive laws relating gradients to fluxes.\n\nConstitutive laws relate gradients in the unknowns to fluxes of quantities\n(for example, heat fluxes are related to temperature gradients through a\nthermal conductivity). This class is a base class for building Underworld\nconstitutive laws.\n\nIn a scalar problem, the relationship is:\n\n.. math::\n\n q_i = k_{ij} \\frac{\\partial T}{\\partial x_j}\n\nand the constitutive parameters describe :math:`k_{ij}`. The template\nassumes :math:`k_{ij} = \\delta_{ij}`.\n\nIn a vector problem (such as the Stokes problem), the relationship is:\n\n.. math::\n\n t_{ij} = c_{ijkl} \\frac{\\partial u_k}{\\partial x_l}\n\nbut is usually written to eliminate the anti-symmetric part of the\ndisplacement or velocity gradients:\n\n.. math::\n\n t_{ij} = c_{ijkl} \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nand the constitutive parameters describe :math:`c_{ijkl}`. The template\nassumes :math:`k_{ij} = \\frac{1}{2}(\\delta_{ik}\\delta_{jl} + \\delta_{il}\\delta_{jk})`\nwhich is the 4th rank identity tensor accounting for symmetry in the flux\nand the gradient terms.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "Constitutive_Model", + "name": "ViscousFlowModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 31, - "signature": "class Constitutive_Model", + "file": "src/underworld3/constitutive_models.py", + "line": 682, + "signature": "class ViscousFlowModel", "parameters": [], "returns": null, - "existing_docstring": "Base class for constitutive models.\n\nConstitutive laws relate gradients in the unknowns to fluxes of quantities\n(e.g., heat fluxes related to temperature gradients via thermal conductivity).\n\nFor scalar problems:\n\n.. math::\n\n q_i = k_{ij} \\frac{\\partial T}{\\partial x_j}\n\nwhere :math:`k_{ij}` are the constitutive parameters. The template assumes\n:math:`k_{ij} = \\delta_{ij}` (identity).\n\nFor vector problems (e.g., Stokes):\n\n.. math::\n\n t_{ij} = c_{ijkl} \\frac{\\partial u_k}{\\partial x_l}\n\nUsually written with symmetrized gradients:\n\n.. math::\n\n t_{ij} = c_{ijkl} \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`c_{ijkl}` are the constitutive parameters. The template assumes\n:math:`c_{ijkl} = \\frac{1}{2}(\\delta_{ik}\\delta_{jl} + \\delta_{il}\\delta_{jk})`,\nthe 4th-rank identity tensor with flux and gradient symmetry.", + "existing_docstring": "Viscous flow constitutive model for Stokes-type solvers.\n\nDefines the relationship between deviatoric stress and strain rate:\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity, which can be a scalar constant, SymPy\nfunction, Underworld mesh variable, or any valid combination. This results\nin an isotropic (but not necessarily homogeneous or linear) relationship\nbetween :math:`\\tau` and the velocity gradients.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (typically velocity and pressure fields).\nmaterial_name : str, optional\n Name identifier for this material (used in multi-material setups).\n\nExamples\n--------\n>>> import underworld3 as uw\n>>> stokes = uw.systems.Stokes(mesh)\n>>> viscous = uw.constitutive_models.ViscousFlowModel(stokes.Unknowns)\n>>> viscous.Parameters.shear_viscosity_0 = 1e21 # Pa.s\n>>> stokes.constitutive_model = viscous\n\nSee Also\n--------\nViscoPlasticFlowModel : Adds yield stress for plastic behavior.\nViscoElasticPlasticFlowModel : Adds viscoelastic memory.", "harvested_comments": [], "status": "partial", "needs": [ @@ -789,14 +1002,14 @@ "is_public": true }, { - "name": "ViscousFlowModel", + "name": "ViscoPlasticFlowModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 259, - "signature": "class ViscousFlowModel", + "file": "src/underworld3/constitutive_models.py", + "line": 906, + "signature": "class ViscoPlasticFlowModel", "parameters": [], "returns": null, - "existing_docstring": "Viscous flow constitutive model.\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity\u2014a scalar constant, sympy function,\nor mesh variable. This gives an isotropic (but not necessarily homogeneous\nor linear) relationship between :math:`\\tau` and velocity gradients. You can\nalso supply :math:`\\eta_{IJ}` (Mandel form) or :math:`\\eta_{ijkl}` (rank-4 tensor).\n\nThe Mandel constitutive matrix is in ``viscous_model.C`` and the rank-4\ntensor form is in ``viscous_model.c``.\n\nExamples\n--------\n>>> viscous_model = ViscousFlowModel(dim)\n>>> viscous_model.material_properties = viscous_model.Parameters(viscosity=viscosity_fn)\n>>> solver.constitutive_model = viscous_model\n>>> tau = viscous_model.flux(gradient_matrix)", + "existing_docstring": "Viscoplastic flow constitutive model with yield stress.\n\nExtends :class:`ViscousFlowModel` with a yield stress that limits the\nmaximum deviatoric stress. When stress would exceed the yield stress,\nthe effective viscosity is reduced to cap the stress.\n\n.. math::\n\n \\tau_{ij} = \\eta_\\mathrm{eff} \\cdot \\dot{\\varepsilon}_{ij}\n\nwhere the effective viscosity is:\n\n.. math::\n\n \\eta_\\mathrm{eff} = \\min\\left(\\eta_0, \\frac{\\tau_y}{2\\dot{\\varepsilon}_{II}}\\right)\n\nand :math:`\\tau_y` is the yield stress and :math:`\\dot{\\varepsilon}_{II}`\nis the second invariant of the strain rate.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (typically velocity and pressure fields).\nmaterial_name : str, optional\n Name identifier for this material.\n\nNotes\n-----\nIf yield stress is not defined, this model behaves identically to\n:class:`ViscousFlowModel`. The message ``not~yet~defined`` in the\neffective viscosity indicates missing parameters.\n\nSee Also\n--------\nViscousFlowModel : Base viscous model without yielding.\nViscoElasticPlasticFlowModel : Adds viscoelastic memory.", "harvested_comments": [], "status": "partial", "needs": [ @@ -806,34 +1019,36 @@ "is_public": true }, { - "name": "ViscoPlasticFlowModel", + "name": "ViscoElasticPlasticFlowModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 344, - "signature": "class ViscoPlasticFlowModel", + "file": "src/underworld3/constitutive_models.py", + "line": 1115, + "signature": "class ViscoElasticPlasticFlowModel", "parameters": [], "returns": null, - "existing_docstring": "Viscoplastic flow constitutive model with yield stress.\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity\u2014a scalar constant, sympy function,\nor mesh variable. This gives an isotropic relationship between :math:`\\tau`\nand velocity gradients. You can also supply :math:`\\eta_{IJ}` (Mandel form)\nor :math:`\\eta_{ijkl}` (rank-4 tensor).\n\nIn a viscoplastic model, the viscosity is defined to cap the overall stress\nat the *yield stress*. This assumes the yield stress is a scalar limit on\nthe 2nd invariant of the stress. Anisotropic models require careful yield\nsurface definition\u2014only a subset of cases is available.\n\nThe Mandel matrix is in ``viscoplastic_model.C`` and the rank-4 tensor in\n``viscoplastic_model.c``.\n\nNotes\n-----\nIf ``not~yet~defined`` appears in the effective viscosity, not all required\nfunctions have been set. The model defaults to standard viscous behavior\nif yield terms are not specified.\n\nExamples\n--------\n>>> viscoplastic_model = ViscoPlasticFlowModel(dim)\n>>> viscoplastic_model.material_properties = viscoplastic_model.Parameters(\n... viscosity=viscosity_fn,\n... yield_stress=yieldstress_fn,\n... min_viscosity=min_viscosity_fn,\n... max_viscosity=max_viscosity_fn,\n... strain_rate_II=strain_rate_inv_fn\n... )\n>>> solver.constitutive_model = viscoplastic_model\n>>> tau = viscoplastic_model.flux(gradient_matrix)", + "existing_docstring": "Viscoelastic-plastic flow constitutive model.\n\nThe stress (flux term) is given by:\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity, a scalar constant, SymPy function,\nUnderworld mesh variable, or any valid combination. This results in an\nisotropic (but not necessarily homogeneous or linear) relationship between\n:math:`\\tau` and the velocity gradients. You can also supply :math:`\\eta_{IJ}`,\nthe Mandel form of the constitutive tensor, or :math:`\\eta_{ijkl}`, the rank-4 tensor.\n\nThe Mandel constitutive matrix is available in `viscous_model.C` and the rank 4 tensor form is\nin `viscous_model.c`. Apply the constitutive model using:", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "ViscoElasticPlasticFlowModel", + "name": "MaxwellExponentialFlowModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 786, - "signature": "class ViscoElasticPlasticFlowModel", + "file": "src/underworld3/constitutive_models.py", + "line": 2025, + "signature": "class MaxwellExponentialFlowModel", "parameters": [], "returns": null, - "existing_docstring": "Viscoelastic-plastic flow constitutive model.\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity\u2014a scalar constant, sympy function,\nor mesh variable. This gives an isotropic relationship between :math:`\\tau`\nand velocity gradients. You can also supply :math:`\\eta_{IJ}` (Mandel form)\nor :math:`\\eta_{ijkl}` (rank-4 tensor).\n\nThe Mandel matrix is in ``viscoelastic_model.C`` and the rank-4 tensor in\n``viscoelastic_model.c``.\n\nExamples\n--------\n>>> viscoelastic_model = ViscoElasticFlowModel(dim)\n>>> viscoelastic_model.material_properties = viscoelastic_model.Parameters(\n... viscosity=viscosity_fn\n... )\n>>> solver.constitutive_model = viscoelastic_model\n>>> tau = viscoelastic_model.flux(gradient_matrix)", + "existing_docstring": "Thin alias: ``ViscoElasticPlasticFlowModel(integrator='etd', order=1)``.\n\n.. deprecated:: Phase B\n Use the canonical form ``ViscoElasticPlasticFlowModel(unknowns,\n integrator='etd', order=1)`` directly. This sibling class\n survives as a thin scaffold so existing scripts continue to\n work; defaults to ETD-1 (recommended).\n\nSee ``docs/developer/design/EXPONENTIAL_VE_INTEGRATOR.md`` for the\nformulation.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, @@ -842,46 +1057,150 @@ { "name": "DiffusionModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1468, + "file": "src/underworld3/constitutive_models.py", + "line": 2049, "signature": "class DiffusionModel", "parameters": [], "returns": null, - "existing_docstring": "Diffusion constitutive model for scalar transport.\n\n.. math::\n\n q_{i} = \\kappa_{ij} \\cdot \\frac{\\partial \\phi}{\\partial x_j}\n\nwhere :math:`\\kappa` is a diffusivity\u2014a scalar constant, sympy function,\nor mesh variable.\n\nExamples\n--------\n>>> diffusion_model = DiffusionModel(dim)\n>>> diffusion_model.material_properties = diffusion_model.Parameters(\n... diffusivity=diffusivity_fn\n... )\n>>> scalar_solver.constitutive_model = diffusion_model\n>>> flux = diffusion_model.flux(gradient_matrix)", + "existing_docstring": "Diffusion (Fourier/Fick) constitutive model for scalar transport.\n\nDefines the flux-gradient relationship for scalar diffusion:\n\n.. math::\n\n q_{i} = \\kappa_{ij} \\frac{\\partial \\phi}{\\partial x_j}\n\nFor isotropic diffusion, :math:`\\kappa_{ij} = \\kappa \\delta_{ij}`.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (the scalar field being diffused).\nmaterial_name : str, optional\n Name identifier for this material.\n\nExamples\n--------\n>>> diffusion = uw.constitutive_models.DiffusionModel(poisson.Unknowns)\n>>> diffusion.Parameters.diffusivity = 1e-6 # m^2/s\n>>> poisson.constitutive_model = diffusion\n\nSee Also\n--------\nAnisotropicDiffusionModel : For direction-dependent diffusivity.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "AnisotropicDiffusionModel", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2159, + "signature": "class AnisotropicDiffusionModel", + "parameters": [], + "returns": null, + "existing_docstring": "Anisotropic diffusion with direction-dependent diffusivities.\n\nDefines a diagonal diffusivity tensor :math:`\\kappa_{ij} = \\text{diag}(\\kappa_0, \\kappa_1, ...)`\nfor direction-dependent diffusion rates.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "GenericFluxModel", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2227, + "signature": "class GenericFluxModel", + "parameters": [], + "returns": null, + "existing_docstring": "A generic constitutive model with symbolic flux expression.\n\nExample usage:\n```python\ngrad_phi = sympy.Matrix([sp.Symbol(\"dphi_dx\"), sp.Symbol(\"dphi_dy\")])\nflux_expr = sympy.Matrix([[kappa_11, kappa_12], [kappa_21, kappa_22]]) * grad_phi\n\nmodel = GenericFluxModel(dim=2)\nmodel.flux = flux_expr\nscalar_solver.constititutive_model = model\n```", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, + { + "name": "DarcyFlowModel", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2304, + "signature": "class DarcyFlowModel", + "parameters": [], + "returns": null, + "existing_docstring": "Darcy flow constitutive model for porous media flow.\n\nThe ``flux`` property returns the assembly flux\n:math:`\\mathbf{F}` that enters the PDE as :math:`-\\nabla\\cdot\\mathbf{F} = f`:\n\n.. math::\n\n F_{i} = \\kappa_{ij} \\left( \\frac{\\partial p}{\\partial x_j} - s_j \\right)\n\nwhere :math:`\\kappa` is the permeability (or hydraulic conductivity),\n:math:`p` is the pressure (or hydraulic head), and :math:`s` is the\nbody force term (e.g., gravity: :math:`s = \\rho g`). The **physical Darcy\nvelocity** is minus this (flow runs *down* the head gradient):\n\n.. math::\n\n q_{i} = -F_{i} = -\\kappa_{ij} \\left( \\frac{\\partial p}{\\partial x_j} - s_j \\right)\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (the pressure/head field).\nmaterial_name : str, optional\n Name identifier for this material.\n\nExamples\n--------\n>>> darcy = uw.constitutive_models.DarcyFlowModel(solver.Unknowns)\n>>> darcy.Parameters.permeability = 1e-12 # m^2\n>>> darcy.Parameters.s = [0, -rho * g] # Gravity in y-direction\n>>> solver.constitutive_model = darcy\n\nSee Also\n--------\nDiffusionModel : For pure diffusion without body forces.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, { "name": "TransverseIsotropicFlowModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1538, + "file": "src/underworld3/constitutive_models.py", + "line": 2451, "signature": "class TransverseIsotropicFlowModel", "parameters": [], "returns": null, - "existing_docstring": "Transversely isotropic viscous flow model.\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere the viscosity tensor :math:`\\eta` is defined as:\n\n.. math::\n\n \\eta_{ijkl} = \\eta_0 I_{ijkl} + (\\eta_0 - \\eta_1) \\left[\n \\frac{1}{2}\\left( n_i n_l \\delta_{jk} + n_j n_k \\delta_{il}\n + n_i n_l \\delta_{jk} + n_j n_l \\delta_{ik} \\right)\n - 2 n_i n_j n_k n_l \\right]\n\nand :math:`\\hat{\\mathbf{n}} \\equiv \\{n_i\\}` is the unit vector defining the\nlocal orientation of the weak plane (the director).\n\nThe Mandel matrix is in ``viscous_model.C`` and the rank-4 tensor in\n``viscous_model.c``.\n\nExamples\n--------\n>>> viscous_model = TransverseIsotropicFlowModel(dim)\n>>> viscous_model.material_properties = viscous_model.Parameters(\n... eta_0=viscosity_fn,\n... eta_1=weak_viscosity_fn,\n... director=orientation_vector_fn\n... )\n>>> solver.constitutive_model = viscous_model\n>>> tau = viscous_model.flux(gradient_matrix)", + "existing_docstring": "Transversely isotropic (anisotropic) viscous flow model.\n\n.. math::\n\n \\tau_{ij} = \\eta_{ijkl} \\cdot \\frac{1}{2} \\left[ \\frac{\\partial u_k}{\\partial x_l}\n + \\frac{\\partial u_l}{\\partial x_k} \\right]\n\nwhere :math:`\\eta` is the viscosity tensor defined as:\n\n.. math::\n\n \\eta_{ijkl} = \\eta_0 \\cdot I_{ijkl} + (\\eta_0-\\eta_1) \\left[ \\frac{1}{2} \\left[\n n_i n_l \\delta_{jk} + n_j n_k \\delta_{il} + n_i n_l \\delta_{jk}\n + n_j n_l \\delta_{ik} \\right] - 2 n_i n_j n_k n_l \\right]\n\nand :math:`\\hat{\\mathbf{n}} \\equiv \\{n_i\\}` is the unit vector defining\nthe local orientation of the weak plane (a.k.a. the director).\n\nThe Mandel constitutive matrix is available in ``viscous_model.C`` and the\nrank-4 tensor form is in ``viscous_model.c``.\n\nExamples\n--------\n>>> viscous_model = TransverseIsotropicFlowModel(dim)\n>>> viscous_model.material_properties = viscous_model.Parameters(\n... eta_0=viscosity_fn,\n... eta_1=weak_viscosity_fn,\n... director=orientation_vector_fn\n... )\n>>> solver.constitutive_model = viscous_model\n>>> tau = viscous_model.flux(gradient_matrix)\n---", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "TransverseIsotropicVEPFlowModel", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2651, + "signature": "class TransverseIsotropicVEPFlowModel", + "parameters": [], + "returns": null, + "existing_docstring": "Transversely isotropic viscoelastic-plastic flow model for fault mechanics.\n\nCombines the anisotropic viscosity tensor from :class:`TransverseIsotropicFlowModel`\nwith viscoelastic stress history and plastic yield limiting on the fault plane.\n\nThe anisotropic viscosity tensor uses two viscosities (\u03b7\u2080 for the bulk,\n\u03b7\u2081 for fault-plane shear) and a director n\u0302 defining the weak plane.\nThe yield stress \u03c4_y limits the shear stress resolved on the fault plane.\n\nParameters\n----------\nunknowns : Unknowns\n Solver unknowns (velocity, pressure).\norder : int, default=1\n Time integration order for stress history (1 or 2).\nmaterial_name : str, optional\n Name for disambiguation in multi-material setups.\n\nSee Also\n--------\nTransverseIsotropicFlowModel : Anisotropic viscous model (no yield/elasticity).\nViscoElasticPlasticFlowModel : Isotropic VEP model.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "TransverseIsotropicMaxwellExponentialFlowModel", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 3504, + "signature": "class TransverseIsotropicMaxwellExponentialFlowModel", + "parameters": [], + "returns": null, + "existing_docstring": "Thin alias: ``TransverseIsotropicVEPFlowModel(integrator='etd', order=1)``.\n\n.. deprecated:: Phase B\n Use the canonical form ``TransverseIsotropicVEPFlowModel(unknowns,\n integrator='etd', order=1)`` directly. This sibling class\n survives as a thin scaffold for existing scripts; defaults to\n ETD-1 (recommended).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "TransverseIsotropicVEPSplitFlowModel", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 3521, + "signature": "class TransverseIsotropicVEPSplitFlowModel", + "parameters": [], + "returns": null, + "existing_docstring": "**EXPERIMENTAL \u2014 DO NOT USE FOR PRODUCTION.**\n\nPhase D investigation artefact. The \u03c3_\u2225 enforcement reaches BDF-class\nfidelity (1.21\u00b7\u03c4_y vs BDF's 1.04\u00b7\u03c4_y at \u03c4_y=0.05) but the velocity\nfield overshoots the boundary value and ratchets monotonically over\ncycles to ~21\u00d7 BDF-1's |u_y|. The fault-tip stress concentrations\nin the PyVista field plot are non-physical for this loading.\n\nRetained on the branch for reproducibility of the investigation; see\n``docs/developer/design/EXPONENTIAL_VE_INTEGRATOR.md`` lessons #9 and\n#10 for why this doesn't ship.\n\nFor deep-yield TI fault problems use\n``TransverseIsotropicVEPFlowModel(integrator='bdf')``.\n\n--\n\nPhase D \u2014 per-component ``(\u03b1_\u22a5, \u03c6_\u22a5)/(\u03b1_\u2225, \u03c6_\u2225)`` ETD-2 for TI VEP.\n\nThe rank-4 modulus splits into two orthogonal projectors:\n\n.. math::\n C(\\eta_0, \\eta_\\parallel) = 2\\eta_0 \\, \\mathbf{P}_\\perp\n + 2\\eta_\\parallel \\, \\mathbf{P}_\\parallel\n\nwhere :math:`\\mathbf{P}_\\parallel` is the director-aligned projector\n(the ``K`` kernel built in :py:meth:`_build_c_tensor`) and\n:math:`\\mathbf{P}_\\perp = \\mathbf{I}_4 - \\mathbf{P}_\\parallel`.\nEach branch has its own Maxwell relaxation time:\n\n.. math::\n \\tau_\\perp = \\eta_0 / \\mu, \\qquad\n \\tau_\\parallel = \\eta_\\parallel^\\text{eff} / \\mu\n\nso the analytical exponential factors differ:\n\n.. math::\n \\alpha_k = e^{-\\Delta t / \\tau_k}, \\qquad\n \\varphi_k = (1 - \\alpha_k) \\tau_k / \\Delta t\n\nThe split flux integrates each branch independently and sums:\n\n.. math::\n \\sigma^{n+1} = (\\alpha_\\perp \\mathbf{P}_\\perp + \\alpha_\\parallel\n \\mathbf{P}_\\parallel) : \\sigma^*\n + 2[\\eta_0(1-\\varphi_\\perp) \\mathbf{P}_\\perp\n + \\eta_\\parallel^\\text{eff}(1-\\varphi_\\parallel)\n \\mathbf{P}_\\parallel] : \\dot\\varepsilon^{n+1}\n + 2[\\eta_0(\\varphi_\\perp - \\alpha_\\perp) \\mathbf{P}_\\perp\n + \\eta_\\parallel^\\text{eff}(\\varphi_\\parallel - \\alpha_\\parallel)\n \\mathbf{P}_\\parallel] : \\dot\\varepsilon^*\n\nPhase B uses a single lumped ``(\u03b1, \u03c6)`` from ``\u03b7_\u2225_eff/\u03bc`` for the\nwhole tensor \u2014 empirically blows up at tight yield surfaces because\nthe matrix branch has no business being yielded. The split scheme\nrelaxes each channel on its proper timescale; the analytical floor\non ``\u03c3_\u2225`` is then ``\u2272 \u03c4_y`` by construction.\n\nImplementation choice: ``\u03b1_\u22a5, \u03c6_\u22a5`` come from the DDt's existing\nscalar ``_exp_coeffs`` (matrix viscosity is fixed and spatially\nuniform \u2014 a single per-step scalar is right). ``\u03b1_\u2225, \u03c6_\u2225`` are\ninlined as sympy expressions of the yield-clipped ``\u03b7_\u2225_eff`` so\nthe JIT evaluates them per quadrature point (spatial heterogeneity\ncaptured automatically). No DDt changes, no solver changes.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "MultiMaterial", + "name": "MultiMaterialConstitutiveModel", "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1686, - "signature": "class MultiMaterial", + "file": "src/underworld3/constitutive_models.py", + "line": 3905, + "signature": "class MultiMaterialConstitutiveModel", "parameters": [], "returns": null, - "existing_docstring": "Manage multiple materials in a constitutive framework.\n\nBundles multiple materials into a single consitutive law. The expectation\nis that these all have compatible flux terms.", + "existing_docstring": "Multi-material constitutive model using level-set weighted flux averaging.\n\nMathematical Foundation:\n\n.. math::\n\n \\mathbf{f}_{\\text{composite}}(\\mathbf{x}) = \\sum_{i=1}^{N}\n \\phi_i(\\mathbf{x}) \\cdot \\mathbf{f}_i(\\mathbf{x})\n\nCritical Architecture:\n\n- Solver owns Unknowns (including :math:`D\\mathbf{F}/Dt` stress history)\n- All constituent models share solver's Unknowns\n- Composite flux becomes stress history for all materials", "harvested_comments": [], "status": "partial", "needs": [ @@ -914,7 +1233,7 @@ "signature": "class CoordinateSystemType", "parameters": [], "returns": null, - "existing_docstring": "Coordinate system types for mesh geometry.\n\nMeshes can have natural coordinate systems that overlay the Cartesian\ncoordinate system used internally for solver assembly. The coordinate\nsystem type determines how vector calculus operators (gradient, divergence,\ncurl) are computed.\n\nAttributes\n----------\nCARTESIAN : int\n Standard Cartesian coordinates (x, y, z).\nCYLINDRICAL2D : int\n 2D cylindrical/polar coordinates (r, theta).\nPOLAR : int\n Alias for CYLINDRICAL2D.\nCYLINDRICAL3D : int\n 3D cylindrical coordinates (r, theta, z).\nSPHERICAL : int\n Spherical coordinates (r, theta, phi).\nGEOGRAPHIC : int\n Ellipsoidal geographic coordinates (lon, lat, depth) for\n Earth and planetary modeling.\n\nSee Also\n--------\nunderworld3.maths.vector_calculus : Operators for each coordinate system.", + "existing_docstring": "Coordinate system types for mesh geometry.\n\nMeshes can have natural coordinate systems that overlay the Cartesian\ncoordinate system used internally for solver assembly. The coordinate\nsystem type determines how vector calculus operators (gradient, divergence,\ncurl) are computed.\n\nSee Also\n--------\nunderworld3.maths.vector_calculus : Operators for each coordinate system.", "harvested_comments": [], "status": "partial", "needs": [ @@ -928,11 +1247,11 @@ "name": "GeographicCoordinateAccessor", "kind": "class", "file": "src/underworld3/coordinates.py", - "line": 491, + "line": 475, "signature": "class GeographicCoordinateAccessor", "parameters": [], "returns": null, - "existing_docstring": "Geographic coordinates on ellipsoidal (WGS84) meshes.\n\nThis class provides natural coordinate access for geographic meshes,\nincluding longitude, latitude, and depth data arrays, symbolic coordinates\nfor equations (:math:`\\lambda_{lon}`, :math:`\\lambda_{lat}`, :math:`\\lambda_d`),\nand ellipsoidal basis vectors.\n\nAccess via ``mesh.X.geo`` on GEOGRAPHIC meshes. Use ``.view()`` for a\ncomplete summary of available properties and methods.\n\nAttributes\n----------\nlon : ndarray\n Longitude in degrees East (-180 to 180)\nlat : ndarray\n Geodetic latitude in degrees North (-90 to 90)\ndepth : ndarray\n Depth below ellipsoid surface in km (positive downward)\ncoords : ndarray\n Combined (N, 3) array: [lon, lat, depth]\n\nExamples\n--------\n>>> geo = mesh.X.geo\n>>> geo.view() # Show all available properties\n>>> geo.coords # (N, 3) array of [lon, lat, depth]\n>>> lon, lat, d = geo[:] # Symbolic coordinates for equations\n>>> geo.unit_down # Geodetic normal (into planet)\n\nNotes\n-----\nThe 'up' direction is the geodetic normal\u2014perpendicular to the ellipsoid\nsurface\u2014NOT the radial direction. At mid-latitudes, these differ by\napproximately 10-11 arcminutes, which matters for regional models.", + "existing_docstring": "Geographic coordinates on ellipsoidal (WGS84) meshes.\n\nThis class provides natural coordinate access for geographic meshes,\nincluding longitude, latitude, and depth data arrays, symbolic coordinates\nfor equations (:math:`\\lambda_{lon}`, :math:`\\lambda_{lat}`, :math:`\\lambda_d`),\nand ellipsoidal basis vectors.\n\nAccess via ``mesh.X.geo`` on GEOGRAPHIC meshes. Use ``.view()`` for a\ncomplete summary of available properties and methods.\n\nExamples\n--------\n>>> geo = mesh.X.geo\n>>> geo.view() # Show all available properties\n>>> geo.coords # (N, 3) array of [lon, lat, depth]\n>>> lon, lat, d = geo[:] # Symbolic coordinates for equations\n>>> geo.unit_down # Geodetic normal (into planet)\n\nNotes\n-----\nThe 'up' direction is the geodetic normal\u2014perpendicular to the ellipsoid\nsurface\u2014NOT the radial direction. At mid-latitudes, these differ by\napproximately 10-11 arcminutes, which matters for regional models.", "harvested_comments": [], "status": "partial", "needs": [ @@ -946,11 +1265,11 @@ "name": "SphericalCoordinateAccessor", "kind": "class", "file": "src/underworld3/coordinates.py", - "line": 945, + "line": 925, "signature": "class SphericalCoordinateAccessor", "parameters": [], "returns": null, - "existing_docstring": "Spherical/polar coordinates for spherical and cylindrical meshes.\n\nThis class provides natural coordinate access for spherical (3D) and\npolar/cylindrical (2D) meshes, including radius, angle data arrays,\nsymbolic coordinates for equations, and basis vectors.\n\nAccess via ``mesh.X.spherical`` on SPHERICAL or CYLINDRICAL2D meshes.\nUse ``.view()`` for a complete summary of available properties and methods.\n\nAttributes\n----------\nr : ndarray\n Radial distance from origin\ntheta : ndarray\n Angle in radians:\n - 3D: Colatitude (0 at north pole, \u03c0 at south pole)\n - 2D: Polar angle from x-axis (standard \u03b8)\nphi : ndarray (3D only)\n Longitude/azimuth in radians (-\u03c0 to \u03c0). Not available for 2D meshes.\ncoords : ndarray\n Combined array: [r, \u03b8, \u03c6] for 3D, [r, \u03b8] for 2D\n\nExamples\n--------\n>>> sph = mesh.X.spherical\n>>> sph.view() # Show all available properties\n>>> sph.coords # (N, 3) or (N, 2) coordinate array\n>>> r, theta = sph[:2] # Works for both 2D and 3D\n>>> sph.unit_r # Radial unit vector (outward)\n\nNotes\n-----\nThe coordinate convention follows physics conventions:\n\n- :math:`r`: Radial distance from origin\n- :math:`\\theta`: Angle (colatitude in 3D, polar angle in 2D)\n- :math:`\\phi`: Longitude/azimuth (3D only)\n\nFor Earth-like applications with geodetic (ellipsoidal) geometry,\nuse a GEOGRAPHIC mesh with ``mesh.X.geo`` instead.", + "existing_docstring": "Spherical/polar coordinates for spherical and cylindrical meshes.\n\nThis class provides natural coordinate access for spherical (3D) and\npolar/cylindrical (2D) meshes, including radius, angle data arrays,\nsymbolic coordinates for equations, and basis vectors.\n\nAccess via ``mesh.X.spherical`` on SPHERICAL or CYLINDRICAL2D meshes.\nUse ``.view()`` for a complete summary of available properties and methods.\n\nExamples\n--------\n>>> sph = mesh.X.spherical\n>>> sph.view() # Show all available properties\n>>> sph.coords # (N, 3) or (N, 2) coordinate array\n>>> r, theta = sph[:2] # Works for both 2D and 3D\n>>> sph.unit_r # Radial unit vector (outward)\n\nNotes\n-----\nThe coordinate convention follows physics conventions:\n\n- :math:`r`: Radial distance from origin\n- :math:`\\theta`: Angle (colatitude in 3D, polar angle in 2D)\n- :math:`\\phi`: Longitude/azimuth (3D only)\n\nFor Earth-like applications with geodetic (ellipsoidal) geometry,\nuse a GEOGRAPHIC mesh with ``mesh.X.geo`` instead.", "harvested_comments": [], "status": "partial", "needs": [ @@ -964,7 +1283,7 @@ "name": "CoordinateSystem", "kind": "class", "file": "src/underworld3/coordinates.py", - "line": 1353, + "line": 1325, "signature": "class CoordinateSystem", "parameters": [], "returns": null, @@ -979,11 +1298,11 @@ "is_public": true }, { - "name": "u", + "name": "consistent_jacobian", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 74, - "signature": "def u(inner_self):", + "line": 185, + "signature": "def consistent_jacobian(self, mode):", "parameters": [], "returns": null, "existing_docstring": null, @@ -997,11 +1316,11 @@ "is_public": true }, { - "name": "u", + "name": "preconditioner", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 78, - "signature": "def u(inner_self, new_u):", + "line": 497, + "signature": "def preconditioner(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1015,11 +1334,11 @@ "is_public": true }, { - "name": "DuDt", + "name": "is_setup", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 95, - "signature": "def DuDt(inner_self):", + "line": 759, + "signature": "def is_setup(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1033,11 +1352,11 @@ "is_public": true }, { - "name": "DuDt", + "name": "u", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 99, - "signature": "def DuDt(inner_self, new_DuDt):", + "line": 816, + "signature": "def u(inner_self, new_u):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1051,12 +1370,12 @@ "is_public": true }, { - "name": "DFDt", + "name": "DuDt", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 105, - "signature": "def DFDt(inner_self):", - "parameters": [], + "line": 838, + "signature": "def DuDt(inner_self, new_DuDt):", + "parameters": [], "returns": null, "existing_docstring": null, "harvested_comments": [], @@ -1072,7 +1391,7 @@ "name": "DFDt", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 109, + "line": 851, "signature": "def DFDt(inner_self, new_DFDt):", "parameters": [], "returns": null, @@ -1087,137 +1406,11 @@ "is_public": true }, { - "name": "E", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 115, - "signature": "def E(inner_self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "L", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 119, - "signature": "def L(inner_self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "W", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 123, - "signature": "def W(inner_self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "Einv2", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 127, - "signature": "def Einv2(inner_self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "CoordinateSystem", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 131, - "signature": "def CoordinateSystem(inner_self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "get_snes_diagnostics", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 165, - "signature": "def get_snes_diagnostics(self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "check_snes_convergence", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 248, - "signature": "def check_snes_convergence(self, raise_on_divergence=True, print_diagnostics=False):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "solve_with_diagnostics", + "name": "F0", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 313, - "signature": "def solve_with_diagnostics(self,\n check_convergence=True,\n raise_on_divergence=False,\n print_diagnostics=False,\n **solve_kwargs):", + "line": 1811, + "signature": "def F0(self):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1231,11 +1424,11 @@ "is_public": true }, { - "name": "F0", + "name": "F1", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 598, - "signature": "def F0(self):", + "line": 1815, + "signature": "def F1(self):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1249,11 +1442,11 @@ "is_public": true }, { - "name": "F1", + "name": "u", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 602, - "signature": "def F1(self):", + "line": 1830, + "signature": "def u(self, new_u):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1267,11 +1460,11 @@ "is_public": true }, { - "name": "u", + "name": "DuDt", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 606, - "signature": "def u(self):", + "line": 1845, + "signature": "def DuDt(self, new_du):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1285,11 +1478,11 @@ "is_public": true }, { - "name": "u", + "name": "DFDt", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 610, - "signature": "def u(self, new_u):", + "line": 1860, + "signature": "def DFDt(self, new_dF):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1303,11 +1496,11 @@ "is_public": true }, { - "name": "DuDt", + "name": "constitutive_model", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 615, - "signature": "def DuDt(self):", + "line": 1881, + "signature": "def constitutive_model(self, model_or_class):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1321,11 +1514,11 @@ "is_public": true }, { - "name": "DuDt", + "name": "constant_nullspace", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 619, - "signature": "def DuDt(self, new_du):", + "line": 2502, + "signature": "def constant_nullspace(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1339,11 +1532,11 @@ "is_public": true }, { - "name": "DFDt", + "name": "petsc_use_constant_nullspace", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 624, - "signature": "def DFDt(self):", + "line": 2571, + "signature": "def petsc_use_constant_nullspace(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1357,11 +1550,11 @@ "is_public": true }, { - "name": "DFDt", + "name": "tolerance", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 628, - "signature": "def DFDt(self, new_dF):", + "line": 2597, + "signature": "def tolerance(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1375,11 +1568,11 @@ "is_public": true }, { - "name": "constitutive_model", + "name": "tolerance", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 634, - "signature": "def constitutive_model(self):", + "line": 3440, + "signature": "def tolerance(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1393,11 +1586,11 @@ "is_public": true }, { - "name": "constitutive_model", + "name": "tolerance", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 638, - "signature": "def constitutive_model(self, model_or_class):", + "line": 4453, + "signature": "def tolerance(self):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1411,11 +1604,11 @@ "is_public": true }, { - "name": "validate_solver", + "name": "tolerance", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 668, - "signature": "def validate_solver(self):", + "line": 4457, + "signature": "def tolerance(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1429,11 +1622,11 @@ "is_public": true }, { - "name": "get_dof_partition", + "name": "p", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 687, - "signature": "def get_dof_partition(self,\n section_type: str,\n filename: Optional[str | None] = None,\n outputPath: Optional[str] = \"\"):", + "line": 5102, + "signature": "def p(inner_self):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1447,11 +1640,11 @@ "is_public": true }, { - "name": "tolerance", + "name": "p", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 908, - "signature": "def tolerance(self):", + "line": 5106, + "signature": "def p(inner_self, new_p):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1468,7 +1661,7 @@ "name": "tolerance", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 912, + "line": 5792, "signature": "def tolerance(self, value):", "parameters": [], "returns": null, @@ -1483,11 +1676,11 @@ "is_public": true }, { - "name": "solve", + "name": "strategy", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1264, - "signature": "def solve(self,\n zero_init_guess: bool =True,\n _force_setup: bool =False,\n verbose: bool=False,\n debug: bool=False,\n debug_name: str=None ):", + "line": 5823, + "signature": "def strategy(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1501,11 +1694,11 @@ "is_public": true }, { - "name": "tolerance", + "name": "PF0", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1513, - "signature": "def tolerance(self):", + "line": 5911, + "signature": "def PF0(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1519,11 +1712,11 @@ "is_public": true }, { - "name": "tolerance", + "name": "p", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1516, - "signature": "def tolerance(self, value):", + "line": 5936, + "signature": "def p(self, new_p):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1537,11 +1730,11 @@ "is_public": true }, { - "name": "solve", + "name": "saddle_preconditioner", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1931, - "signature": "def solve(self,\n zero_init_guess: bool =True,\n _force_setup: bool =False,\n verbose=False,\n debug=False,\n debug_name=None,\n ):", + "line": 5956, + "signature": "def saddle_preconditioner(self, function):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1555,11 +1748,11 @@ "is_public": true }, { - "name": "p", + "name": "petsc_use_nullspace", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2138, - "signature": "def p(inner_self):", + "line": 5974, + "signature": "def petsc_use_nullspace(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1573,11 +1766,11 @@ "is_public": true }, { - "name": "p", + "name": "petsc_use_pressure_nullspace", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2142, - "signature": "def p(inner_self, new_p):", + "line": 6025, + "signature": "def petsc_use_pressure_nullspace(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1591,11 +1784,11 @@ "is_public": true }, { - "name": "tolerance", + "name": "multiplier_schur_pc", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2335, - "signature": "def tolerance(self):", + "line": 6066, + "signature": "def multiplier_schur_pc(self, value):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1609,11 +1802,11 @@ "is_public": true }, { - "name": "tolerance", + "name": "petsc_velocity_nullspace_basis", "kind": "method", "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2339, - "signature": "def tolerance(self, value):", + "line": 6121, + "signature": "def petsc_velocity_nullspace_basis(self, modes):", "parameters": [], "returns": null, "existing_docstring": null, @@ -1627,233 +1820,224 @@ "is_public": true }, { - "name": "strategy", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2351, - "signature": "def strategy(self):", + "name": "Integral", + "kind": "class", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 34, + "signature": "class Integral:", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n The `Integral` class constructs the volume integral\n\n .. math:: F_{i} = \\int_V \\, f(\\mathbf{x}) \\, \\mathrm{d} V\n\n for some scalar function :math:`f` over the mesh domain :math:`V`.\n\n Parameters\n ----------\n mesh :\n The mesh over which integration is performed.\n fn :\n Function to be integrated.\n\n Example\n -------\n Calculate volume of mesh:\n\n >>> import underworld3 as uw\n >>> import numpy as np\n >>> mesh = uw.discretisation.Box()\n >>> volumeIntegral = uw.maths.Integral(mesh=mesh, fn=1.)\n >>> np.allclose( 1., volumeIntegral.evaluate(), rtol=1e-8)\n True\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "strategy", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2355, - "signature": "def strategy(self, value):", + "name": "CellWiseIntegral", + "kind": "class", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 227, + "signature": "class CellWiseIntegral:", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n Compute volume integrals over each mesh cell individually.\n\n The ``CellWiseIntegral`` class constructs cell-by-cell volume integrals:\n\n .. math:: F_c = \\\\int_{V_c} \\\\, f(\\\\mathbf{x}) \\\\, \\\\mathrm{d} V\n\n for some scalar function :math:`f` over each cell volume :math:`V_c`.\n\n Unlike :class:`Integral` which returns a single scalar over the entire\n mesh domain, this class returns an array with one value per mesh cell.\n\n Parameters\n ----------\n mesh : underworld3.discretisation.Mesh\n The mesh over which integration is performed.\n fn : float, int, or sympy.Basic\n Function to be integrated.\n\n See Also\n --------\n Integral : For domain-wide (global) volume integrals.\n", "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], + "status": "complete", + "needs": [], "parent_class": null, "is_public": true }, { - "name": "PF0", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2417, - "signature": "def PF0(self):", + "name": "BdIntegral", + "kind": "class", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 340, + "signature": "class BdIntegral:", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n The ``BdIntegral`` class constructs the boundary (surface/line) integral\n\n .. math:: F = \\int_{\\partial \\Omega} \\, f(\\mathbf{x}, \\mathbf{n}) \\, \\mathrm{d} S\n\n for some scalar function :math:`f` over a named boundary :math:`\\partial \\Omega`\n of the mesh. In 2D this is a line integral; in 3D a surface integral.\n\n The integrand may reference the outward unit normal via ``mesh.Gamma_N``\n (components map to ``petsc_n[0], petsc_n[1], ...`` in the generated C code).\n\n Parameters\n ----------\n mesh : underworld3.discretisation.Mesh\n The mesh whose boundary is integrated over.\n fn : float, int, or sympy.Basic\n Scalar function to be integrated.\n boundary : str\n Name of the boundary label (e.g. ``\"Top\"``, ``\"Bottom\"``, ``\"Internal\"``).\n Must be present in ``mesh.boundaries``.\n\n Example\n -------\n Calculate length of the top boundary of a unit box:\n\n >>> import underworld3 as uw\n >>> import numpy as np\n >>> mesh = uw.discretisation.Box()\n >>> bdIntegral = uw.maths.BdIntegral(mesh=mesh, fn=1., boundary=\"Top\")\n >>> np.allclose( 1., bdIntegral.evaluate(), rtol=1e-8)\n True\n\n See Also\n --------\n Integral : For volume integrals over the entire mesh domain.\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "PF0", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2421, - "signature": "def PF0(self, value):", + "name": "Mesh", + "kind": "class", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 207, + "signature": "class Mesh", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Unstructured mesh with PETSc DMPlex backend.\n\nThe Mesh class provides the spatial discretisation for finite element\ncomputations. It wraps PETSc's DMPlex for unstructured mesh management,\nsupporting various cell types (triangles, quadrilaterals, tetrahedra,\nhexahedra) and coordinate systems.\n\nParameters\n----------\nplex_or_meshfile : PETSc.DMPlex or str\n Either a PETSc DMPlex object or path to a mesh file (gmsh, exodus).\ndegree : int, optional\n Polynomial degree for the coordinate field (default 1).\nsimplex : bool, optional\n True for simplicial elements (triangles/tets), False for quads/hexes.\ncoordinate_system_type : CoordinateSystemType, optional\n Coordinate system for vector calculus (Cartesian, cylindrical, etc.).\nqdegree : int, optional\n Quadrature degree for numerical integration (default 2).\nboundaries : list of NamedTuple, optional\n Boundary region definitions with names and values.\nboundary_normals : dict, optional\n Outward normal vectors for each boundary.\nunits : str or pint.Unit, optional\n Deprecated and ignored (DeprecationWarning). Mesh coordinate units\n always come from the model's reference quantities\n (``model.set_reference_quantities``); query them via ``mesh.units``.\nverbose : bool, optional\n Print mesh construction information.\n\nExamples\n--------\nMeshes are typically created via the meshing module::\n\n >>> mesh = uw.meshing.UnstructuredSimplexBox(\n ... minCoords=(0, 0), maxCoords=(1, 1), cellSize=0.1\n ... )\n >>> T = mesh.add_variable(\"T\", vtype=uw.VarType.SCALAR)\n\nSee Also\n--------\nunderworld3.meshing : Mesh generation utilities.\nunderworld3.discretisation.MeshVariable : Field variables on meshes.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "p", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2427, - "signature": "def p(self):", + "name": "EnhancedMeshVariable", + "kind": "class", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 37, + "signature": "class EnhancedMeshVariable", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Enhanced MeshVariable with mathematical operations, units support, and persistence.\n\nThis class enhances the base MeshVariable with:\n- Mathematical operations (via MathematicalMixin) - direct arithmetic, component access\n- Units support (via DimensionalityMixin) - dimensional analysis, unit conversion\n- Optional persistence for adaptive meshing scenarios\n- Collision-safe registration with qualified naming\n- All original MeshVariable functionality (via controlled delegation)\n\nThe wrapper uses controlled delegation to avoid assignment issues with\noperators like += while providing seamless access to the underlying\nMeshVariable functionality.\n\nExamples:\n # Basic enhanced variable\n velocity = EnhancedMeshVariable(\"velocity\", mesh, 2, units=\"m/s\")\n\n # Mathematical operations\n momentum = density * velocity\n v_x = velocity[0]\n speed = velocity.norm(\"L2\")\n\n # Persistence for adaptive meshing\n persistent_var = EnhancedMeshVariable(\"pressure\", mesh, 1, persistent=True)\n success = persistent_var.transfer_data_from(old_pressure)", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "p", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2431, - "signature": "def p(self, new_p):", + "name": "RemeshPolicy", + "kind": "class", + "file": "src/underworld3/discretisation/remesh.py", + "line": 56, + "signature": "class RemeshPolicy", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Per-variable transfer semantics on a mesh adapt.\n\n``REMAP`` is the safe universal default \u2014 \"the previous values are\nthose at the original mesh points,\" correct to first order or better\nfor any Eulerian quantity. Anything not classified stays here so a\nforgotten variable fails *safe* (transferred needlessly) rather than\nsilently wrong (stale).", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "saddle_preconditioner", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2436, - "signature": "def saddle_preconditioner(self):", + "name": "RemeshContext", + "kind": "class", + "file": "src/underworld3/discretisation/remesh.py", + "line": 97, + "signature": "class RemeshContext", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "State passed to operator ``on_remesh`` hooks.\n\nCarries the geometric data an operator (typically a ``DuDt``) needs\nto update its own history coherently \u2014 old/new coordinates, total\ndisplacement across the mover sweep, ``dt``, and a scratch slot\nwhere an ALE-style operator stashes ``v_mesh`` for the next solve\nto consume. The framework has already handled the generic\nper-variable REMAP pass before hooks are fired.\n\n``managed_snapshot`` holds pre-move ``.data`` for every\noperator-managed variable (vars with ``_remesh_managed_by`` set).\nThe default CARRY path doesn't need it \u2014 ``.data`` is left\nuntouched. A hook that needs to fall back to REMAP (e.g. on an OT\nreset, see ``ale_opt_out``) calls\n:func:`remap_var_set` with this dict and its own var list.\n\n``scratch`` is a free-form dict where adapt ops or hooks publish\nflags / per-step state (e.g. ``ale_opt_out``, a stashed\n``v_mesh``). Keys are by convention.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "saddle_preconditioner", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2440, - "signature": "def saddle_preconditioner(self, function):", + "name": "CachedDMInterpolationInfo", + "kind": "class", + "file": "src/underworld3/function/_dminterp_wrapper.pyx", + "line": 40, + "signature": "cdef class CachedDMInterpolationInfo:", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n Python-managed wrapper for DMInterpolationInfo C structure.\n\n This class ensures:\n 1. Proper cleanup via __dealloc__ (Python GC calls this)\n 2. Keeps referenced arrays alive (coords, cells)\n 3. Provides safe access to the C structure\n\n Attributes\n ----------\n _ipInfo : DMInterpolationInfo\n The actual C structure (opaque pointer)\n coords : ndarray\n Coordinates used to build this structure (kept alive)\n cells : ndarray\n Cell hints used for setup (kept alive)\n dofcount : int\n Total DOF count this structure was built for\n is_valid : bool\n Whether the structure is valid (not yet destroyed)\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "validate_solver", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2500, - "signature": "def validate_solver(self):", + "name": "UnderworldAppliedFunction", + "kind": "class", + "file": "src/underworld3/function/_function.pyx", + "line": 51, + "signature": "class UnderworldAppliedFunction(sympy.core.function.AppliedUndef):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n Applied Underworld function representing a mesh variable evaluated at coordinates.\n\n This class extends SymPy's AppliedUndef to represent Underworld mesh variables\n in symbolic expressions. When a mesh variable's symbolic representation (e.g.,\n ``velocity.sym``) appears in an expression, it is an instance of this class.\n\n The class provides:\n\n - Type identification via ``isinstance(obj, UnderworldAppliedFunction)``\n - Custom derivative handling through :meth:`fdiff`\n - LaTeX rendering that shows the coordinate system (Cartesian or curvilinear)\n - Access to the parent mesh variable via the ``meshvar`` weakref on the class\n\n Notes\n -----\n Users typically don't instantiate this class directly. It is created\n automatically when accessing ``mesh_variable.sym`` or when mesh variables\n appear in symbolic expressions.\n\n See Also\n --------\n UnderworldFunction : The metaclass that creates these applied function classes.\n UnderworldAppliedFunctionDeriv : Derivative version of this class.\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "get_dof_partition", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2522, - "signature": "def get_dof_partition(self,\n section_type: str,\n filename: Optional[str | None] = None,\n outputPath: Optional[str] = \"\"):", + "name": "UnderworldAppliedFunctionDeriv", + "kind": "class", + "file": "src/underworld3/function/_function.pyx", + "line": 109, + "signature": "class UnderworldAppliedFunctionDeriv(UnderworldAppliedFunction):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n First derivative of an Underworld mesh variable function.\n\n This class represents spatial derivatives of mesh variables (e.g.,\n :math:`\\\\partial T / \\\\partial x`). Instances are created automatically\n when differentiating mesh variable symbols using ``sympy.diff()``.\n\n The derivative can be evaluated numerically via the standard evaluation\n functions, which use either Clement gradient recovery (fast, approximate)\n or L2 projection (accurate, requires solve).\n\n Notes\n -----\n Second derivatives are not currently supported. Attempting to differentiate\n an instance of this class will raise a RuntimeError.\n\n See Also\n --------\n UnderworldAppliedFunction : The parent class for undifferentiated functions.\n", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "solve", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 3092, - "signature": "def solve(self,\n zero_init_guess: bool = True,\n picard: int = 0,\n verbose=False,\n debug=False,\n debug_name=None,\n _force_setup: bool =False, ):", + "name": "UnderworldFunction", + "kind": "class", + "file": "src/underworld3/function/_function.pyx", + "line": 134, + "signature": "class UnderworldFunction(sympy.Function):", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n Metaclass that returns programmatic class objects rather than instances.\n\n This basically follows the pattern of the ``sympy.Function``\n metaclass, with two key differences. First, we set\n ``UnderworldAppliedFunction`` as the base class, which allows\n ``isinstance(someobj, UnderworldAppliedFunction)`` checks.\n Second, we grab a weakref of the owning meshvariable onto the\n class itself (not the instance), because SymPy internally uses\n ``type(obj)(obj.args)`` to clone instances and extra info must\n be on the class so that clones are complete.\n\n Consider the calling pattern\n\n >>> newfn = UnderworldFunction(meshvar,name)(*meshvar.mesh.r)\n\n This is equivalent to\n\n >>> newfnclass = UnderworldFunction(meshvar,name) # Here we create a new *class*.\n >>> newfn = newfnclass(*meshvar.mesh.r) # Here we create an instance of the class.\n\n Parameters\n ----------\n name : str\n The name of the function.\n meshvar : MeshVariable\n The mesh variable corresponding to this function.\n vtype : VarType\n The variable type (scalar, vector, etc).\n component : int or tuple\n For vector functions, this is the component of the vector.\n For example, component ``1`` might correspond to ``v_y``.\n For tensors, the component is a tuple.\n For scalars, this value is ignored.\n", "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], + "status": "complete", + "needs": [], "parent_class": null, "is_public": true }, { - "name": "Mesh", + "name": "sympy_function_printable", "kind": "class", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 149, - "signature": "class Mesh", + "file": "src/underworld3/function/analytic.pyx", + "line": 30, + "signature": "class sympy_function_printable(sympy.Function):", "parameters": [], "returns": null, - "existing_docstring": "Unstructured mesh with PETSc DMPlex backend.\n\nThe Mesh class provides the spatial discretisation for finite element\ncomputations. It wraps PETSc's DMPlex for unstructured mesh management,\nsupporting various cell types (triangles, quadrilaterals, tetrahedra,\nhexahedra) and coordinate systems.\n\nParameters\n----------\nplex_or_meshfile : PETSc.DMPlex or str\n Either a PETSc DMPlex object or path to a mesh file (gmsh, exodus).\ndegree : int, optional\n Polynomial degree for the coordinate field (default 1).\nsimplex : bool, optional\n True for simplicial elements (triangles/tets), False for quads/hexes.\ncoordinate_system_type : CoordinateSystemType, optional\n Coordinate system for vector calculus (Cartesian, cylindrical, etc.).\nqdegree : int, optional\n Quadrature degree for numerical integration (default 2).\nboundaries : list of NamedTuple, optional\n Boundary region definitions with names and values.\nboundary_normals : dict, optional\n Outward normal vectors for each boundary.\nunits : str or pint.Unit, optional\n Physical units for mesh coordinates.\nverbose : bool, optional\n Print mesh construction information.\n\nAttributes\n----------\nN : sympy.vector.CoordSys3D\n SymPy coordinate system for symbolic expressions.\nX : UWCoordinate tuple\n Coordinate variables (x, y, z) for use in expressions.\ndim : int\n Spatial dimension of the mesh.\ndm : PETSc.DMPlex\n Underlying PETSc distributed mesh object.\n\nExamples\n--------\nMeshes are typically created via the meshing module::\n\n >>> mesh = uw.meshing.UnstructuredSimplexBox(\n ... minCoords=(0, 0), maxCoords=(1, 1), cellSize=0.1\n ... )\n >>> T = mesh.add_variable(\"T\", vtype=uw.VarType.SCALAR)\n\nSee Also\n--------\nunderworld3.meshing : Mesh generation utilities.\nunderworld3.discretisation.MeshVariable : Field variables on meshes.", + "existing_docstring": "\n This help function simply does most of the work for c-code printing.\n Inherit from this and set `self._printstr` and `self._header` as necessary.\n See `AnalyticSolNL` for example.\n", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "EnhancedMeshVariable", + "name": "SolCx", "kind": "class", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 37, - "signature": "class EnhancedMeshVariable", + "file": "src/underworld3/function/analytic.pyx", + "line": 159, + "signature": "class SolCx:", "parameters": [], "returns": null, - "existing_docstring": "Enhanced MeshVariable with mathematical operations, units support, and persistence.\n\nThis class enhances the base MeshVariable with:\n- Mathematical operations (via MathematicalMixin) - direct arithmetic, component access\n- Units support (via DimensionalityMixin) - dimensional analysis, unit conversion\n- Optional persistence for adaptive meshing scenarios\n- Collision-safe registration with qualified naming\n- All original MeshVariable functionality (via controlled delegation)\n\nThe wrapper uses controlled delegation to avoid assignment issues with\noperators like += while providing seamless access to the underlying\nMeshVariable functionality.\n\nExamples:\n # Basic enhanced variable\n velocity = EnhancedMeshVariable(\"velocity\", mesh, 2, units=\"m/s\")\n\n # Mathematical operations\n momentum = density * velocity\n v_x = velocity[0]\n speed = velocity.norm(\"L2\")\n\n # Persistence for adaptive meshing\n persistent_var = EnhancedMeshVariable(\"pressure\", mesh, 1, persistent=True)\n success = persistent_var.transfer_data_from(old_pressure)", + "existing_docstring": "Velic *SolCx* analytic Stokes solution \u2014 the canonical free-slip,\n discontinuous-viscosity benchmark: a viscosity step :math:`\\eta_A` (for\n :math:`xx_c`), driven by the density\n forcing :math:`f=(0,\\cos(\\pi x)\\sin(n\\pi z))` on the unit box with free-slip\n walls. Use it to validate a UW3 Stokes solve against the exact solution::\n\n sol = uw.function.analytic.SolCx(mesh, eta_A=1.0, eta_B=1.0e6, x_c=0.5, n=1)\n stokes.constitutive_model.Parameters.shear_viscosity_0 = sol.fn_viscosity\n stokes.bodyforce = sol.fn_bodyforce\n # free-slip on all four walls + pressure nullspace, then solve and:\n rel = sol.velocity_error(stokes.u) # L2 error vs the analytic\n\n Notes\n -----\n ``fn_velocity`` / ``fn_pressure`` wrap the compiled Velic kernel and are\n evaluated point-wise via SymPy ``evalf`` (they are not ``lambdify``-able, so\n use :meth:`evaluate_velocity` / :meth:`velocity_error` rather than\n ``uw.function.evaluate``). ``fn_bodyforce`` / ``fn_viscosity`` are elementary\n and compile through the normal JIT path when assigned to a solver.\n\n The body-force **sign** matches UW3's momentum convention; the original\n Underworld2 documentation quotes the opposite sign.\n", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, @@ -1881,7 +2065,7 @@ "name": "UWexpression", "kind": "class", "file": "src/underworld3/function/expressions.py", - "line": 533, + "line": 605, "signature": "class UWexpression", "parameters": [], "returns": null, @@ -1896,7 +2080,7 @@ "name": "UWDerivativeExpression", "kind": "class", "file": "src/underworld3/function/expressions.py", - "line": 1657, + "line": 1746, "signature": "class UWDerivativeExpression", "parameters": [], "returns": null, @@ -1926,23 +2110,6 @@ "parent_class": null, "is_public": true }, - { - "name": "KDTree", - "kind": "class", - "file": "src/underworld3/kdtree.py", - "line": 18, - "signature": "class KDTree", - "parameters": [], - "returns": null, - "existing_docstring": "Unit-aware KDTree for spatial indexing and queries.\n\nThis class extends pykdtree.KDTree to handle coordinate units properly:\n- Stores coordinate units when constructed from unit-aware data\n- Automatically converts query coordinates to match the KD-tree's units\n- Returns distances with appropriate units (when not squared)\n- Compatible with both unit-aware and plain numpy arrays\n\nUsage:\n # Create KD-tree from mesh with units\n mesh = uw.meshing.StructuredQuadBox(..., units=\"km\")\n kd = uw.kdtree.KDTree(mesh.points)\n\n # Query with coordinates (units will be converted if needed)\n query_pts = np.array([[100.0, 50.0]]) # Can have units or not\n distances, indices = kd.query(query_pts)", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, { "name": "MaterialProperty", "kind": "class", @@ -2064,6 +2231,23 @@ "parent_class": null, "is_public": true }, + { + "name": "BoundingSurface", + "kind": "class", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 41, + "signature": "class BoundingSurface", + "parameters": [], + "returns": null, + "existing_docstring": "One bounding surface of a mesh \u2014 its geometry, slip state, and methods.\n\nParameters\n----------\nmesh : underworld3 mesh\n The mesh this surface belongs to.\nlabel : str\n The boundary label (a name in ``mesh.boundaries``) this surface is for.\nkind : {\"radial\", \"plane\", \"facet\", \"free\"}\n ``radial`` \u2014 snap to ``|r| = radius`` about ``centre`` (annulus / sphere\n / cylinder); ``plane`` \u2014 project onto the plane through ``point`` with\n unit ``normal`` (box face); ``facet`` \u2014 nearest reference facet\n (follow-up); ``free`` \u2014 follow the live deformed surface, analytic\n restore is a no-op (follow-up).\ncentre, radius : for ``radial``.\npoint, normal : for ``plane``.\nis_free : bool\n If True (or ``kind == \"free\"``), :meth:`restore` is a no-op \u2014 the\n surface follows the live discrete boundary rather than a fixed target.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, { "name": "FaultSurface", "kind": "class", @@ -2086,7 +2270,7 @@ "name": "FaultCollection", "kind": "class", "file": "src/underworld3/meshing/faults.py", - "line": 376, + "line": 374, "signature": "class FaultCollection", "parameters": [], "returns": null, @@ -2104,7 +2288,7 @@ "name": "SurfaceVariable", "kind": "class", "file": "src/underworld3/meshing/surfaces.py", - "line": 71, + "line": 367, "signature": "class SurfaceVariable", "parameters": [], "returns": null, @@ -2122,7 +2306,7 @@ "name": "Surface", "kind": "class", "file": "src/underworld3/meshing/surfaces.py", - "line": 317, + "line": 611, "signature": "class Surface", "parameters": [], "returns": null, @@ -2140,7 +2324,7 @@ "name": "SurfaceCollection", "kind": "class", "file": "src/underworld3/meshing/surfaces.py", - "line": 1251, + "line": 1951, "signature": "class SurfaceCollection", "parameters": [], "returns": null, @@ -2193,7 +2377,7 @@ "name": "ThermalConvectionConfig", "kind": "class", "file": "src/underworld3/model.py", - "line": 4549, + "line": 4726, "signature": "class ThermalConvectionConfig", "parameters": [], "returns": null, @@ -2296,7 +2480,7 @@ "name": "SwarmType", "kind": "class", "file": "src/underworld3/swarm.py", - "line": 52, + "line": 49, "signature": "class SwarmType", "parameters": [], "returns": null, @@ -2314,11 +2498,11 @@ "name": "SwarmVariable", "kind": "class", "file": "src/underworld3/swarm.py", - "line": 82, + "line": 75, "signature": "class SwarmVariable", "parameters": [], "returns": null, - "existing_docstring": "Variable supported by a particle swarm (point cloud).\n\nA SwarmVariable stores values at discrete particle locations and provides\na mesh-based proxy representation for use in symbolic expressions. This\nenables Lagrangian tracking of material properties through deformation.\n\nParameters\n----------\nname : str\n Identifier for this variable (must be unique within the swarm).\nswarm : Swarm\n The supporting particle swarm.\nsize : int or tuple, optional\n Shape specification: int for vectors, tuple for matrices.\n If None, inferred from ``vtype``.\nvtype : VarType, optional\n Variable type (SCALAR, VECTOR, TENSOR, SYM_TENSOR, MATRIX).\n If None, inferred from ``size``.\ndtype : type, default=float\n Data type for storage (float or int).\nproxy_degree : int, default=1\n Polynomial degree for the mesh proxy variable.\nproxy_continuous : bool, default=True\n Whether the proxy uses continuous (True) or discontinuous (False)\n interpolation.\nvarsymbol : str, optional\n LaTeX symbol for display. Defaults to ``name``.\nrebuild_on_cycle : bool, default=True\n If True, rebuild the proxy when particles cycle through periodic\n boundaries. Recommended for continuous fields.\nunits : str or pint.Unit, optional\n Physical units for this variable (e.g., 'kelvin', 'Pa').\n Requires reference quantities to be set on the model.\n\nAttributes\n----------\ndata : numpy.ndarray\n Direct access to variable values at particle locations.\nsym : sympy.Matrix\n Symbolic representation for use in expressions.\n\nSee Also\n--------\nMeshVariable : Variable supported by mesh nodes.\nSwarm : Container for particle locations.\n\nExamples\n--------\nCreate a temperature field on a swarm:\n\n>>> swarm = uw.swarm.Swarm(mesh)\n>>> T = swarm.add_variable(\"T\", size=1, vtype=uw.VarType.SCALAR)\n>>> T.data[:] = 1600.0 # Set initial temperature\n\nCreate a velocity field:\n\n>>> v = swarm.add_variable(\"v\", size=mesh.dim, vtype=uw.VarType.VECTOR)\n\nNotes\n-----\nSwarmVariables are essential for tracking material properties that\nadvect with the flow. The mesh proxy enables their use in finite\nelement formulations while particle storage preserves Lagrangian\nhistory.", + "existing_docstring": "Variable supported by a particle swarm (point cloud).\n\nA SwarmVariable stores values at discrete particle locations and provides\na mesh-based proxy representation for use in symbolic expressions. This\nenables Lagrangian tracking of material properties through deformation.\n\nParameters\n----------\nname : str\n Identifier for this variable (must be unique within the swarm).\nswarm : Swarm\n The supporting particle swarm.\nsize : int or tuple, optional\n Shape specification: int for vectors, tuple for matrices.\n If None, inferred from ``vtype``.\nvtype : VarType, optional\n Variable type (SCALAR, VECTOR, TENSOR, SYM_TENSOR, MATRIX).\n If None, inferred from ``size``.\ndtype : type, default=float\n Data type for storage (float or int).\nproxy_degree : int, default=1\n Polynomial degree for the mesh proxy variable.\nproxy_continuous : bool, default=True\n Whether the proxy uses continuous (True) or discontinuous (False)\n interpolation.\nvarsymbol : str, optional\n LaTeX symbol for display. Defaults to ``name``.\nrebuild_on_cycle : bool, default=True\n No effect. Retained for backward compatibility with the removed\n particle-recycling (streak swarm) feature.\nunits : str or pint.Unit, optional\n Physical units for this variable (e.g., 'kelvin', 'Pa').\n Requires reference quantities to be set on the model.\n\nSee Also\n--------\nMeshVariable : Variable supported by mesh nodes.\nSwarm : Container for particle locations.\n\nExamples\n--------\nCreate a temperature field on a swarm:\n\n>>> swarm = uw.swarm.Swarm(mesh)\n>>> T = swarm.add_variable(\"T\", size=1, vtype=uw.VarType.SCALAR)\n>>> T.data[:] = 1600.0 # Set initial temperature\n\nCreate a velocity field:\n\n>>> v = swarm.add_variable(\"v\", size=mesh.dim, vtype=uw.VarType.VECTOR)\n\nNotes\n-----\nSwarmVariables are essential for tracking material properties that\nadvect with the flow. The mesh proxy enables their use in finite\nelement formulations while particle storage preserves Lagrangian\nhistory.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2331,11 +2515,11 @@ "name": "IndexSwarmVariable", "kind": "class", "file": "src/underworld3/swarm.py", - "line": 1903, + "line": 2146, "signature": "class IndexSwarmVariable", "parameters": [], "returns": null, - "existing_docstring": "Integer-valued swarm variable for material tracking.\n\nIndexSwarmVariable stores integer indices at particle locations, typically\nused for tracking distinct material types. It automatically generates\nsymbolic mask expressions for each material index, enabling material-\ndependent properties in constitutive models.\n\nParameters\n----------\nname : str\n Variable name for identification and I/O.\nswarm : Swarm\n Parent swarm object.\nindices : int\n Number of distinct material indices (default 1).\nproxy_degree : int\n Polynomial degree for mesh projection (default 1).\nproxy_continuous : bool\n Whether mesh proxy is continuous (default True).\n\nAttributes\n----------\nsym : list of sympy.Expr\n Symbolic mask expressions for each material index.\n\nExamples\n--------\n>>> material = IndexSwarmVariable(\"M\", swarm, indices=3)\n>>> material.data[:] = 0 # Set all particles to material 0\n>>> # Use sym[i] as multiplier for material i properties\n>>> viscosity = material.sym[0] * 1e20 + material.sym[1] * 1e21\n\nSee Also\n--------\nSwarmVariable : Base class for particle-supported variables.", + "existing_docstring": "Integer-valued swarm variable for material tracking.\n\nIndexSwarmVariable stores integer indices at particle locations, typically\nused for tracking distinct material types. It automatically generates\nsymbolic mask expressions for each material index, enabling material-\ndependent properties in constitutive models.\n\nParameters\n----------\nname : str\n Variable name for identification and I/O.\nswarm : Swarm\n Parent swarm object.\nindices : int\n Number of distinct material indices (default 1).\nproxy_degree : int\n Polynomial degree for mesh projection (default 1).\nproxy_continuous : bool\n Whether mesh proxy is continuous (default True).\n\nExamples\n--------\n>>> material = IndexSwarmVariable(\"M\", swarm, indices=3)\n>>> material.data[:] = 0 # Set all particles to material 0\n>>> # Use sym[i] as multiplier for material i properties\n>>> viscosity = material.sym[0] * 1e20 + material.sym[1] * 1e21\n\nSee Also\n--------\nSwarmVariable : Base class for particle-supported variables.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2348,11 +2532,11 @@ "name": "Swarm", "kind": "class", "file": "src/underworld3/swarm.py", - "line": 2213, + "line": 2603, "signature": "class Swarm", "parameters": [], "returns": null, - "existing_docstring": "A basic particle swarm implementation for Lagrangian particle tracking and data storage.\n\nThe UW `Swarm` class provides a simplified particle management system that uses\nPETSc's DMSWARM_BASIC type. Unlike the standard `Swarm` class, this implementation\ndoes not rely on PETSc to determine ranks for particle migration but instead uses\nour own kdtree neighbour-domain computations.\n\nThis class is preferred for most operations except where particle / cell relationships\nare always required.\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The mesh object that defines the computational domain for particle operations.\n Particles will be associated with this mesh for spatial queries and operations.\nrecycle_rate : int, optional\n Rate at which particles are recycled for streak management. If > 1, enables\n streak particle functionality where particles are duplicated and tracked\n across multiple cycles. Default is 0 (no recycling).\nverbose : bool, optional\n Enable verbose output for debugging and monitoring particle operations.\n Default is False.\n\nAttributes\n----------\nmesh : uw.discretisation.Mesh\n Reference to the associated mesh object.\ndim : int\n Spatial dimension of the mesh (2D or 3D).\ncdim : int\n Coordinate dimension of the mesh.\ndata : numpy.ndarray\n Direct access to particle coordinate data.\nparticle_coordinates : SwarmVariable\n SwarmVariable containing particle coordinate information.\nrecycle_rate : int\n Current recycle rate for streak management.\ncycle : int\n Current cycle number for streak particles.\n\nMethods\n-------\npopulate(fill_param=1)\n Populate the swarm with particles throughout the domain.\nmigrate(remove_sent_points=True, delete_lost_points=True, max_its=10)\n Manually migrate particles across MPI processes after coordinate updates.\nadd_particles_with_coordinates(coords)\n Add new particles at specified coordinate locations.\nadd_particles_with_global_coordinates(coords)\n Add particles using global coordinate system.\nadd_variable(name, size, dtype=float)\n Add a new variable to track additional particle properties.\nsave(filename, meshUnits=1.0, swarmUnits=1.0, units=\"dimensionless\")\n Save swarm data to file.\nread_timestep(filename, step_name, outputPath=\"./output/\")\n Read swarm data from a specific timestep file.\nadvection(V_fn, delta_t, evalf=False, corrector=True, restore_points_func=None)\n Advect particles using a velocity field.\nestimate_dt(V_fn, dt_min=1.0e-15, dt_max=1.0)\n Estimate appropriate timestep for particle advection.\n\nExamples\n--------\nCreate a basic swarm and populate with particles:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(0,0), maxCoords=(1,1))\n>>> swarm = uw.swarm.Swarm(mesh=mesh)\n>>> swarm.populate(fill_param=2)\n\nCreate a streak swarm with recycling:\n\n>>> streak_swarm = uw.swarm.Swarm(mesh=mesh, recycle_rate=5)\n>>> streak_swarm.populate(fill_param=1)\n\nAdd custom particle data:\n\n>>> temperature = swarm.add_variable(\"temperature\", 1)\n>>> velocity = swarm.add_variable(\"velocity\", mesh.dim)\n\nManual particle migration after coordinate updates:\n\nNote: particle migration is still called automatically when we\n`access` and update the particle_coordinates variables\n\nNote: `swarm.populate` uses a the mesh point locations for discontinuous interpolants to\ndetermine the particle locations.", + "existing_docstring": "A basic particle swarm implementation for Lagrangian particle tracking and data storage.\n\nThe UW `Swarm` class provides a simplified particle management system that uses\nPETSc's DMSWARM_BASIC type. Unlike the standard `Swarm` class, this implementation\ndoes not rely on PETSc to determine ranks for particle migration but instead uses\nour own kdtree neighbour-domain computations.\n\nThis class is preferred for most operations except where particle / cell relationships\nare always required.\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The mesh object that defines the computational domain for particle operations.\n Particles will be associated with this mesh for spatial queries and operations.\nrecycle_rate : int, optional\n Particle recycling (streak swarms) is NOT implemented: values > 1\n raise ``NotImplementedError``. The parameter is retained so that\n existing calls passing the default (0 or 1, meaning no recycling)\n keep working.\nverbose : bool, optional\n Enable verbose output for debugging and monitoring particle operations.\n Default is False.\n\nExamples\n--------\nCreate a basic swarm and populate with particles:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(0,0), maxCoords=(1,1))\n>>> swarm = uw.swarm.Swarm(mesh=mesh)\n>>> swarm.populate(fill_param=2)\n\nAdd custom particle data:\n\n>>> temperature = swarm.add_variable(\"temperature\", 1)\n>>> velocity = swarm.add_variable(\"velocity\", mesh.dim)\n\nParticle migration after coordinate updates:\n\nNote: writing particle coordinates (via ``swarm._particle_coordinates.data``\nor the ``coords`` setter) marks the swarm for migration; the collective\n``migrate()`` itself is DEFERRED to the next collective point \u2014 a\n``migration_control()`` context exit, an explicit ``swarm.migrate()``,\nor solve entry \u2014 never run per-write (uneven writes would deadlock).\n\nNote: `swarm.populate` uses a the mesh point locations for discontinuous interpolants to\ndetermine the particle locations.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2365,11 +2549,11 @@ "name": "NodalPointSwarm", "kind": "class", "file": "src/underworld3/swarm.py", - "line": 4301, + "line": 5076, "signature": "class NodalPointSwarm", "parameters": [], "returns": null, - "existing_docstring": "BASIC_Swarm with particles located at the coordinate points of a meshVariable\n\nThe swarmVariable `X0` is defined so that the particles can \"snap back\" to their original locations\nafter they have been moved.\n\nThe purpose of this Swarm is to manage sample points for advection schemes based on upstream sampling\n(method of characteristics etc)", + "existing_docstring": "BASIC_Swarm with particles located at the coordinate points of a meshVariable\n\n.. deprecated:: 2026-07\n ``NodalPointSwarm`` is deprecated and will be removed in the next\n release cycle. The semi-Lagrangian history managers in\n ``uw.systems.ddt`` no longer use it and there are no remaining\n internal callers.\n\nThe swarmVariable `X0` is defined so that the particles can \"snap back\" to their original locations\nafter they have been moved.\n\nThe purpose of this Swarm is to manage sample points for advection schemes based on upstream sampling\n(method of characteristics etc)", "harvested_comments": [], "status": "partial", "needs": [ @@ -2380,14 +2564,14 @@ "is_public": true }, { - "name": "SwarmPICLayout", + "name": "DDtSymbolicState", "kind": "class", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 25, - "signature": "class SwarmPICLayout", + "file": "src/underworld3/systems/ddt.py", + "line": 105, + "signature": "class DDtSymbolicState", "parameters": [], "returns": null, - "existing_docstring": "Particle population fill type:\n\nSwarmPICLayout.REGULAR defines points on a regular ijk mesh. Supported by simplex cell types only.\nSwarmPICLayout.GAUSS defines points using an npoint Gauss-Legendre tensor product quadrature rule.\nSwarmPICLayout.SUBDIVISION defines points on the centroid of a sub-divided reference cell.", + "existing_docstring": "Snapshot of a :class:`Symbolic` DDt instance's evolution state.\n\n``Symbolic`` is the pure-symbolic flavor \u2014 ``psi_star`` history\nslots hold sympy expressions (immutable), captured by value.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2398,31 +2582,32 @@ "is_public": true }, { - "name": "PICSwarm", + "name": "DDtEulerianState", "kind": "class", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 39, - "signature": "class PICSwarm", + "file": "src/underworld3/systems/ddt.py", + "line": 116, + "signature": "class DDtEulerianState", "parameters": [], "returns": null, - "existing_docstring": "Particle swarm implementation with automatic mesh-particle interactions.\n\nThe `Swarm` class is Underworld's primary particle management system, built on PETSc's\nDMSWARM_PIC type. It provides automatic particle migration, mesh-particle connectivity,\nand streamlined particle operations for Lagrangian particle tracking and data storage.\n\nDifferences from UW Swarm:\n- **Mesh Integration**: Built-in particle-in-cell (PIC) connectivity with automatic cell tracking\n- **Migration**: Uses the standard PETSc strategy for migration which depends on the DM type. This requires\n calculation of cell-relationships each time the coordinates are updated and particles that are not found will\n be deleted.\n\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The mesh object that defines the computational domain. Particles will be\n automatically associated with mesh cells for efficient spatial operations.\nrecycle_rate : int, optional\n Rate at which particles are recycled for streak management. If > 1, enables\n streak particle functionality where particles are duplicated and tracked\n across multiple cycles. Default is 0 (no recycling).\nverbose : bool, optional\n Enable verbose output for debugging and monitoring particle operations.\n Default is False.\n\nAttributes\n----------\nmesh : uw.discretisation.Mesh\n Reference to the associated mesh object.\ndim : int\n Spatial dimension of the mesh (2D or 3D).\ncdim : int\n Coordinate dimension of the mesh.\ndata : numpy.ndarray\n Direct access to particle coordinate data.\nparticle_coordinates : SwarmVariable\n SwarmVariable containing particle coordinate information (auto-created).\nparticle_cellid : SwarmVariable\n SwarmVariable containing particle cell ID information (auto-created).\nrecycle_rate : int\n Current recycle rate for streak management.\ncycle : int\n Current cycle number for streak particles.\n\nMethods\n-------\npopulate_petsc(fill_param=1)\n Populate swarm using PETSc's built-in particle generation.\npopulate(fill_param=1, layout=SwarmPICLayout.GAUSS)\n Populate the swarm with particles using specified layout.\nadd_particles_with_coordinates(coords)\n Add new particles at specified coordinate locations.\nadd_variable(name, size, dtype=float)\n Add a new variable to track additional particle properties.\nsave(filename, meshUnits=1.0, swarmUnits=1.0, units=\"dimensionless\")\n Save swarm data to file.\nread_timestep(filename, step_name, outputPath=\"./output/\")\n Read swarm data from a specific timestep file.\nadvection(V_fn, delta_t, evalf=False, corrector=True, restore_points_func=None)\n Advect particles using a velocity field with automatic migration.\nestimate_dt(V_fn, dt_min=1.0e-15, dt_max=1.0)\n Estimate appropriate timestep for particle advection.\n\nExamples\n--------\nCreate a standard swarm with automatic features:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(0,0), maxCoords=(1,1))\n>>> swarm = uw.swarm.Swarm(mesh=mesh)\n>>> swarm.populate(fill_param=2, layout=uw.swarm.SwarmPICLayout.GAUSS)\n\nAccess automatic coordinate and cell ID fields:\n\n>>> coords = swarm._particle_coordinates.data\n>>> cell_ids = swarm.particle_cellid.data\n\nCreate a streak swarm with recycling:\n\n>>> streak_swarm = uw.swarm.Swarm(mesh=mesh, recycle_rate=5)\n>>> streak_swarm.populate(fill_param=1)\n\nAdd custom particle data and perform advection:\n\n>>> temperature = swarm.add_variable(\"temperature\", 1)\n>>> velocity_field = mesh.add_variable(\"velocity\", mesh.dim)\n>>> # ... set up velocity field ...\n>>> swarm.advection(velocity_field.sym, delta_t=0.01) # Automatic migration\n\nNotes\n-----\n- Particle migration occurs automatically during advection operations\n- Coordinate and cell ID fields are created and managed automatically at the\n PETSc level", + "existing_docstring": "Snapshot of an :class:`Eulerian` DDt instance.\n\n``psi_star`` is a list of :class:`MeshVariable` objects; their\nDOF arrays travel via the mesh-variable snapshot path. This State\nonly records the variable names for restore-side verification\nthat the binding still holds.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "NodalPointPICSwarm", + "name": "DDtSemiLagrangianState", "kind": "class", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1387, - "signature": "class NodalPointPICSwarm", + "file": "src/underworld3/systems/ddt.py", + "line": 129, + "signature": "class DDtSemiLagrangianState", "parameters": [], "returns": null, - "existing_docstring": "Swarm with particles located at the coordinate points of a meshVariable\n\nThe swarmVariable `X0` is defined so that the particles can \"snap back\" to their original locations\nafter they have been moved.\n\nThe purpose of this Swarm is to manage sample points for advection schemes based on upstream sampling\n(method of characteristics etc)", + "existing_docstring": "Snapshot of a :class:`SemiLagrangian` DDt instance.\n\nLike :class:`DDtEulerianState`, plus an optional ``forcing_star``\nvariable (when ``with_forcing_history=True``) used by ETD-2\nintegration of the Maxwell relaxation operator.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2433,14 +2618,14 @@ "is_public": true }, { - "name": "Symbolic", + "name": "DDtLagrangianState", "kind": "class", "file": "src/underworld3/systems/ddt.py", - "line": 50, - "signature": "class Symbolic", + "line": 143, + "signature": "class DDtLagrangianState", "parameters": [], "returns": null, - "existing_docstring": "Symbolic history manager for time derivative approximations.\n\nManages the update of a variable :math:`\\psi` across timesteps. The history\noperator stores :math:`\\psi` over several timesteps (given by ``order``) so\nthat it can compute backward differentiation (BDF) or Adams-Moulton expressions.\n\nThe history operator is defined as:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} &\\leftarrow \\psi_p^{t-(n-1)\\Delta t} \\\\\n \\psi_p^{t-(n-1)\\Delta t} &\\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots \\\\\n \\psi_p^{t-\\Delta t} &\\leftarrow \\psi_p^{t}", + "existing_docstring": "Snapshot of a :class:`Lagrangian` DDt instance.\n\n``psi_star`` is a list of :class:`SwarmVariable` objects on this\nDDt's internal swarm. Their data travels via the swarm-variable\nsnapshot path.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2451,14 +2636,14 @@ "is_public": true }, { - "name": "Eulerian", + "name": "DDtLagrangianSwarmState", "kind": "class", "file": "src/underworld3/systems/ddt.py", - "line": 240, - "signature": "class Eulerian", + "line": 155, + "signature": "class DDtLagrangianSwarmState", "parameters": [], "returns": null, - "existing_docstring": "Eulerian (mesh-based) history manager.\n\nManages the update of a variable :math:`\\psi` on the mesh across timesteps:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} &\\leftarrow \\psi_p^{t-(n-1)\\Delta t} \\\\\n \\psi_p^{t-(n-1)\\Delta t} &\\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots \\\\\n \\psi_p^{t-\\Delta t} &\\leftarrow \\psi_p^{t}", + "existing_docstring": "Snapshot of a :class:`Lagrangian_Swarm` DDt instance.\n\nSame shape as :class:`DDtLagrangianState`; the difference is\noperational (Lagrangian creates its own swarm, Lagrangian_Swarm\nuses a user-provided one) rather than state-shaped.", "harvested_comments": [], "status": "partial", "needs": [ @@ -2469,119 +2654,82 @@ "is_public": true }, { - "name": "SemiLagrangian", + "name": "Symbolic", "kind": "class", "file": "src/underworld3/systems/ddt.py", - "line": 537, - "signature": "class SemiLagrangian", + "line": 463, + "signature": "class Symbolic", "parameters": [], "returns": null, - "existing_docstring": "Semi-Lagrangian history manager using nodal swarm.\n\nManages the semi-Lagrangian update of a mesh variable :math:`\\psi`\nacross timesteps:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} &\\leftarrow \\psi_p^{t-(n-1)\\Delta t} \\\\\n \\psi_p^{t-(n-1)\\Delta t} &\\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots \\\\\n \\psi_p^{t-\\Delta t} &\\leftarrow \\psi_p^{t}", + "existing_docstring": "Symbolic history manager for time derivative approximations.\n\nManages the update of a variable :math:`\\psi` across timesteps. The history\noperator stores :math:`\\psi` over several timesteps (given by ``order``) so\nthat it can compute backward differentiation (BDF) or Adams-Moulton expressions.\n\nThe history operator is defined as:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} &\\leftarrow \\psi_p^{t-(n-1)\\Delta t} \\\\\n \\psi_p^{t-(n-1)\\Delta t} &\\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots \\\\\n \\psi_p^{t-\\Delta t} &\\leftarrow \\psi_p^{t}\n\nThis is a purely symbolic history manager that operates on sympy expressions\nwithout mesh or swarm storage. It is useful for tracking symbolic expressions\nthrough time-stepping algorithms.\n\nParameters\n----------\npsi_fn : sympy.Basic\n The sympy expression to track. Can be scalar or matrix form.\ntheta : float, optional\n Implicitness parameter for Adams-Moulton order 1 (default ``0.5``).\n Values: 0 = explicit, 1 = implicit, 0.5 = Crank-Nicolson.\nvarsymbol : str, optional\n LaTeX symbol for display (default ``r\"\\\\psi\"``).\nverbose : bool, optional\n Enable verbose output (default ``False``).\nbcs : list, optional\n Boundary conditions (default ``[]``).\norder : int, optional\n Order of time integration (1-3) (default ``1``).\nsmoothing : float, optional\n Smoothing parameter (default ``0.0``).\n\nNotes\n-----\nThe ``Symbolic`` class is the base for understanding BDF and Adams-Moulton\nformulas without the complexity of mesh or swarm storage. It is primarily\nuseful for:\n\n- Understanding time-stepping algorithm behavior\n- Debugging symbolic expressions in time-dependent problems\n- Prototyping before implementing with mesh/swarm storage\n\nFor actual simulations, use ``Eulerian``, ``SemiLagrangian``, or\n``Lagrangian`` which store history on computational meshes or swarms.\n\nSee Also\n--------\nEulerian : Mesh-based history with BDF time-stepping.\nSemiLagrangian : Nodal-swarm approach for advection-dominated problems.\nLagrangian : Swarm-based material tracking.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "Lagrangian", + "name": "Eulerian", "kind": "class", "file": "src/underworld3/systems/ddt.py", - "line": 1146, - "signature": "class Lagrangian", + "line": 807, + "signature": "class Eulerian", "parameters": [], "returns": null, - "existing_docstring": "Swarm-based Lagrangian history manager.\n\nManages the update of a Lagrangian variable :math:`\\psi` on the swarm\nacross timesteps:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} \\leftarrow \\psi_p^{t-(n-1)\\Delta t}\n\n \\psi_p^{t-(n-1)\\Delta t} \\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots\n\n \\psi_p^{t-\\Delta t} \\leftarrow \\psi_p^{t}", + "existing_docstring": "Eulerian (mesh-based) history manager for time derivatives.\n\nManages the update of a variable :math:`\\psi` on the mesh across timesteps,\nstoring history values on mesh variables for backward differentiation.\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} &\\leftarrow \\psi_p^{t-(n-1)\\Delta t} \\\\\n \\psi_p^{t-(n-1)\\Delta t} &\\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots \\\\\n \\psi_p^{t-\\Delta t} &\\leftarrow \\psi_p^{t}\n\nWhen ``V_fn`` is provided, the ``update_pre_solve`` method applies an\nexplicit advection correction so that ``bdf()`` approximates the full\nmaterial derivative :math:`D\\psi/Dt = \\partial\\psi/\\partial t + \\mathbf{u}\\cdot\\nabla\\psi`\nrather than the partial time derivative alone.\n\n.. note::\n The optional advection capability (V_fn parameter) is suitable for\n problems where the advection is weak or where a purely grid-based\n approach is desired (e.g., Richards equation with no transport).\n For advection-dominated problems, SemiLagrangian is more mature and\n generally preferred.\n\nParameters\n----------\nmesh : underworld3.discretisation.Mesh\n The computational mesh.\npsi_fn : MeshVariable or sympy.Basic\n The quantity to track. Can be a mesh variable or symbolic expression.\nvtype : VarType\n Variable type (SCALAR, VECTOR, etc.) for history storage.\ndegree : int\n Polynomial degree for history mesh variables.\ncontinuous : bool\n Whether history variables are continuous across element boundaries.\nV_fn : sympy.Basic, optional\n Velocity field for grid-based advection correction.\n If None (default), computes pure \u2202\u03c8/\u2202t. If set, computes\n D/Dt = \u2202/\u2202t + u\u00b7\u2207 via operator splitting.\nevalf : bool, default=False\n If True, evaluate expressions numerically during updates.\ntheta : float, default=0.5\n Time-stepping parameter for implicit/explicit blending.\n theta=0 is fully explicit, theta=1 is fully implicit.\nvarsymbol : str, default=r\"u\"\n LaTeX symbol for display.\nverbose : bool, default=False\n Enable verbose output during updates.\nbcs : list, default=[]\n Boundary conditions to apply to projections.\norder : int, default=1\n Number of history timesteps to store (for multi-step methods).\nsmoothing : float, default=0.0\n Smoothing parameter for projections.\n\nSee Also\n--------\nSemiLagrangian : For advection-dominated problems with nodal swarm.\nLagrangian : For full Lagrangian tracking on swarms.\nSymbolic : For purely symbolic history (no mesh storage).", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "Lagrangian_Swarm", + "name": "SemiLagrangian", "kind": "class", "file": "src/underworld3/systems/ddt.py", - "line": 1356, - "signature": "class Lagrangian_Swarm", + "line": 1309, + "signature": "class SemiLagrangian", "parameters": [], "returns": null, - "existing_docstring": "Swarm-based Lagrangian history manager (user-provided swarm).\n\nManages the update of a Lagrangian variable :math:`\\psi` on a user-supplied\nswarm across timesteps:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} \\leftarrow \\psi_p^{t-(n-1)\\Delta t}\n\n \\psi_p^{t-(n-1)\\Delta t} \\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots\n\n \\psi_p^{t-\\Delta t} \\leftarrow \\psi_p^{t}", + "existing_docstring": "Semi-Lagrangian history manager using nodal swarm.\n\nManages the semi-Lagrangian update of a mesh variable :math:`\\psi`\nacross timesteps. Uses a nodal swarm to track departure points and\ninterpolate values back to the mesh.\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} &\\leftarrow \\psi_p^{t-(n-1)\\Delta t} \\\\\n \\psi_p^{t-(n-1)\\Delta t} &\\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots \\\\\n \\psi_p^{t-\\Delta t} &\\leftarrow \\psi_p^{t}\n\nThe semi-Lagrangian method traces characteristics backward in time\nto find departure points, providing stable advection without CFL\nrestrictions while maintaining accuracy.\n\nParameters\n----------\nmesh : underworld3.discretisation.Mesh\n The computational mesh.\npsi_fn : sympy.Function\n The quantity to advect (typically a mesh variable's symbolic form).\nV_fn : sympy.Function\n Velocity field for advection (e.g., ``stokes.u.sym``).\nvtype : VarType\n Variable type (SCALAR, VECTOR, SYM_TENSOR, etc.).\ndegree : int\n Polynomial degree for mesh variable storage.\ncontinuous : bool\n Whether variables are continuous across element boundaries.\nswarm_degree : int, optional\n Polynomial degree for swarm interpolation. Defaults to ``degree``.\nswarm_continuous : bool, optional\n Continuity for swarm variables. Defaults to ``continuous``.\nvarsymbol : str, optional\n LaTeX symbol for display.\nverbose : bool, default=False\n Enable verbose output during updates.\nbcs : list, default=[]\n Boundary conditions for projections.\norder : int, default=1\n Number of history timesteps and, for the time-derivative operator,\n the order of the BDF backward-difference stencil taken *along the\n characteristic*:\n\n - ``order = 1`` \u2192 ``[1, -1]``: single-step difference\n ``(\u03c8^{n+1} - \u03c8*)/\u0394t``. Paired with a trapezoidal (Crank-Nicolson,\n ``theta=0.5``) flux this is the standard **SLCN** scheme \u2014 second-\n order accurate even though the stencil and the departure point\n are first-order, because the trapezoidal-along-the-trajectory\n structure recovers the order (Spiegelman & Katz, 2006).\n - ``order = 2`` \u2192 ``[3/2, -2, 1/2]``: BDF2 stencil\n ``(3/2 \u03c8^{n+1} - 2 \u03c8* + 1/2 \u03c8**)/\u0394t``, using two departure\n points. This is the **SL-BDF2** scheme. BDF2 is a one-sided\n implicit method: it expects the *flux evaluated at* ``n+1``\n only, i.e. a Backward-Euler-centred flux (``theta=1.0``), **not**\n Crank-Nicolson. SL-BDF2 reaches the same second order as SLCN but\n avoids the spurious resonance/ringing CN can show on stiff modes\n (Bonaventura et al., 2021).\n\n .. important::\n BDF (time-derivative) and Adams-Moulton/\u03b8 (flux) are *distinct*\n multistep families and must be **paired consistently**: SLCN =\n ``order=1`` + ``theta=0.5``; SL-BDF2 = ``order=2`` + ``theta=1.0``.\n Mixing a BDF2 stencil with a Crank-Nicolson flux (``order=2`` +\n ``theta=0.5``) centres the two sides at different times and is\n **not** a consistent second-order scheme. In\n :class:`~underworld3.systems.solvers.SNES_AdvectionDiffusion` the\n advective ``DuDt`` carries this BDF ``order`` while the diffusive\n ``DFDt`` carries the \u03b8-method flux \u2014 set them as a matched pair.\nsmoothing : float, default=0.0\n Smoothing parameter for projections.\npreserve_moments : bool, default=False\n Not implemented. Passing ``True`` raises ``NotImplementedError``.\n (The moment-preserving projection this promised was never wired in;\n the parameter is retained so existing call signatures keep working.)\nwith_forcing_history : bool, default=False\n When True, allocate an additional ``forcing_star`` MeshVariable\n (matching ``psi_star[0]``'s shape, vtype, degree, continuity) to\n store one history slot for the strain-rate forcing. Required by\n ETD-2 exponential integration of the Maxwell relaxation operator;\n ignored for BDF/AM. Populated each step via\n :meth:`update_forcing_history` (direct nodal evaluation of\n ``forcing_fn`` \u2014 typically the constitutive model's strain-rate\n symbol).\ntheta : float, default=0.5\n Adams-Moulton \u03b8 for the implicit flux integrator at order 1.\n The order-1 AM coefficients are ``[\u03b8, 1-\u03b8]``:\n\n - ``\u03b8 = 0.5`` \u2192 Crank-Nicolson (trapezoidal, second-order\n accurate, A-stable). Default, matches legacy SLCN behaviour.\n - ``\u03b8 = 1.0`` \u2192 Backward Euler (L-stable, monotone for\n diffusion, first-order accurate). Use for stiff parabolic\n terms (under-resolved sharp gradients on deformed cells)\n where CN's lack of L-stability causes sign-flip ringing\n on stiff modes.\n\n Settable after construction as a property:\n ``adv_diff.DuDt.theta = 1.0``.\n\nNotes\n-----\nThe semi-Lagrangian method is particularly useful for:\n\n- Advection-dominated problems (high P\u00e9clet number)\n- Problems where CFL stability is restrictive\n- Viscoelastic stress advection\n\nThe time-derivative (BDF ``order``) and the diffusive flux integrator\n(Adams-Moulton ``theta``) are separate choices that must be paired\nconsistently \u2014 see ``order`` and ``theta`` above and the discussion in\n``docs/advanced/semi-lagrangian-time-integration.md``.\n\nReferences\n----------\nSpiegelman, M., & Katz, R. F. (2006). A semi-Lagrangian Crank-Nicolson\nalgorithm for the numerical solution of advection-diffusion problems.\n*Geochemistry, Geophysics, Geosystems*, 7(4).\nhttps://doi.org/10.1029/2005GC001073\n\nBonaventura, L., Calzola, E., Carlini, E., & Ferretti, R. (2021).\nSecond order fully semi-Lagrangian discretizations of\nadvection-diffusion-reaction systems. *Journal of Scientific Computing*,\n88, 23. https://doi.org/10.1007/s10915-021-01518-8 \u2014 SL-BDF2 reaches\nsecond order while avoiding the spurious resonance of CN-type schemes.\n\nSee Also\n--------\nEulerian : For fixed-mesh time derivatives without advection.\nLagrangian : For full particle-following Lagrangian tracking.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "UnitsError", - "kind": "class", - "file": "src/underworld3/units.py", - "line": 25, - "signature": "class UnitsError", - "parameters": [], - "returns": null, - "existing_docstring": "Exception raised for units-related errors.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "DimensionalityError", + "name": "Lagrangian", "kind": "class", - "file": "src/underworld3/units.py", - "line": 31, - "signature": "class DimensionalityError", + "file": "src/underworld3/systems/ddt.py", + "line": 2920, + "signature": "class Lagrangian", "parameters": [], "returns": null, - "existing_docstring": "Exception raised for dimensional inconsistency errors.", + "existing_docstring": "Swarm-based Lagrangian history manager for material tracking.\n\nManages the update of a Lagrangian variable :math:`\\psi` on a swarm\nacross timesteps. Creates and manages its own internal swarm for\ntracking material properties through the flow.\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} \\leftarrow \\psi_p^{t-(n-1)\\Delta t}\n\n \\psi_p^{t-(n-1)\\Delta t} \\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots\n\n \\psi_p^{t-\\Delta t} \\leftarrow \\psi_p^{t}\n\nThe Lagrangian approach follows material points through the flow,\navoiding numerical diffusion in advection. History values are stored\non swarm variables with proxy mesh variables for solver integration.\n\nParameters\n----------\nmesh : underworld3.discretisation.Mesh\n The computational mesh.\npsi_fn : sympy.Function\n The quantity to track (e.g., stress tensor ``stokes.stress``).\nV_fn : sympy.Function\n Velocity field for particle advection.\nvtype : VarType\n Variable type (SCALAR, VECTOR, SYM_TENSOR, etc.).\ndegree : int\n Polynomial degree for proxy mesh variables.\ncontinuous : bool\n Whether proxy variables are continuous across elements.\nvarsymbol : str, default=r\"u\"\n LaTeX symbol for display.\nverbose : bool, default=False\n Enable verbose output during updates.\nbcs : list, default=[]\n Boundary conditions (currently unused for swarm variables).\norder : int, default=1\n Number of history timesteps to store.\nsmoothing : float, default=0.0\n Smoothing parameter for projections.\nfill_param : int, default=3\n Fill parameter for swarm population density.\n\nNotes\n-----\nThe Lagrangian method is ideal for:\n\n- Viscoelastic stress history tracking\n- Material property advection without diffusion\n- Tracking compositional fields\n\nThe internal swarm is automatically advected during updates.\n\nSee Also\n--------\nSemiLagrangian : Mesh-based semi-Lagrangian with departure points.\nLagrangian_Swarm : For user-provided swarms.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": true }, { - "name": "NoUnitsError", + "name": "Lagrangian_Swarm", "kind": "class", - "file": "src/underworld3/units.py", - "line": 37, - "signature": "class NoUnitsError", - "parameters": [], - "returns": null, - "existing_docstring": "Exception raised when units are expected but not found.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "add_condition", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 405, - "signature": "def add_condition(self, f_id, c_type, conds, label, components=None):", + "file": "src/underworld3/systems/ddt.py", + "line": 3292, + "signature": "class Lagrangian_Swarm", "parameters": [], "returns": null, - "existing_docstring": "\n Add a dirichlet or neumann condition to the mesh.\n\n This function prepares UW data to use PetscDSAddBoundary().\n\n Parameters\n ----------\n f_id: int\n Index of the solver's field (equation) to apply the condition.\n Note: The solvers field id is usually different to the mesh's field ids.\n c_type: string\n BC type. Either dirichlet (essential) or neumann (natural) conditions.\n conds: array_like of floats or a sympy.Matrix\n eg. For a 3D model with an unconstraint x component: (None, 5, 1.2) or sympy.Matrix([sympy.oo, 5, 1.2])\n label: string\n The label name to apply the BC. To find a label/boundary name run something like\n mesh.view()\n components: array_like, single int value or None.\n (optional) tuple, or int of active conds components to use. Use 'None' for all conds to be used.\n If 'None' and components in 'cond' equal sympy.oo or -sympy.oo those components won't be used.\n eg. For the 3D example cond = (2, 5, 1.2), components = (1,2) the x components is ignored and uncontrainted.\n", + "existing_docstring": "Swarm-based Lagrangian history manager (user-provided swarm).\n\nManages the update of a Lagrangian variable :math:`\\psi` on a user-supplied\nswarm across timesteps:\n\n.. math::\n\n \\psi_p^{t-n\\Delta t} \\leftarrow \\psi_p^{t-(n-1)\\Delta t}\n\n \\psi_p^{t-(n-1)\\Delta t} \\leftarrow \\psi_p^{t-(n-2)\\Delta t} \\cdots\n\n \\psi_p^{t-\\Delta t} \\leftarrow \\psi_p^{t}\n\nUnlike ``Lagrangian``, this class uses a user-provided swarm rather than\ncreating its own. The swarm should already be populated and configured\nfor tracking material points.\n\nParameters\n----------\nswarm : underworld3.swarm.Swarm\n User-provided swarm for material point tracking.\npsi_fn : sympy.Function\n The quantity to track (e.g., stress tensor from a solver).\nvtype : underworld3.VarType\n Variable type (SCALAR, VECTOR, SYM_TENSOR, etc.).\ndegree : int\n Interpolation degree for proxy mesh variables.\ncontinuous : bool\n Whether proxy mesh variables should be continuous.\nvarsymbol : str, optional\n LaTeX symbol for display (default ``\"u\"``).\nverbose : bool, optional\n Enable verbose output (default ``False``).\nbcs : list, optional\n Boundary conditions (currently unused, default ``[]``).\norder : int, optional\n Order of time integration (1-3) (default ``1``).\nsmoothing : float, optional\n Smoothing parameter (default ``0.0``).\nstep_averaging : int, optional\n Number of steps for history averaging (default ``2``).\n\nAttributes\n----------\nmesh : underworld3.discretisation.Mesh\n Reference to the computational mesh (from swarm).\nswarm : underworld3.swarm.Swarm\n The user-provided swarm.\npsi_fn : sympy.Function\n Symbolic expression for the tracked quantity.\norder : int\n Order of BDF integration.\nstep_averaging : int\n Number of steps for averaging (affects BDF scaling).\npsi_star : list\n History values :math:`\\psi^*, \\psi^{**}, \\ldots` as swarm variables.\n\nNotes\n-----\nKey differences from ``Lagrangian`` class:\n\n- Uses user-provided swarm (not internally created)\n- Swarm advection is NOT performed (user's responsibility)\n- Step averaging for smoothing history updates\n- Suitable when swarm is shared between multiple history managers\n\nThe ``step_averaging`` parameter scales the BDF formula to account for\nupdates that occur over multiple sub-steps within a timestep.\n\nSee Also\n--------\nLagrangian : Creates and manages its own swarm with advection.\nSemiLagrangian : Nodal-swarm approach for advection-dominated problems.\nEulerian : Pure mesh-based history (no particle tracking).", "harvested_comments": [], "status": "partial", "needs": [ @@ -2591,187 +2739,121 @@ "is_public": true }, { - "name": "is_numeric_only", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 538, - "signature": "def is_numeric_only(val):", - "parameters": [], - "returns": null, - "existing_docstring": "Check if value is a pure number (no symbols).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "add_essential_bc", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 576, - "signature": "def add_essential_bc(self, conds, boundary, components=None):", - "parameters": [], - "returns": null, - "existing_docstring": "\n see add_condtion() docstring\n", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "add_natural_bc", + "name": "storage", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 584, - "signature": "def add_natural_bc(self, conds, boundary, components=None):", - "parameters": [], - "returns": null, - "existing_docstring": "\n see add_condtion() docstring\n", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/systems/solvers.py", + "line": 690, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": null, - "is_public": true - }, - { - "name": "add_dirichlet_bc", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 591, - "signature": "def add_dirichlet_bc(self, conds, boundary, components=None):", - "parameters": [], "returns": null, - "existing_docstring": "\n see add_condtion() docstring\n", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "get_local_field_is", + "name": "delta_t", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 3197, - "signature": "def get_local_field_is(section, field, unconstrained=False):", - "parameters": [], - "returns": null, - "existing_docstring": "\n This function returns the index set of unconstrained points if True, or all points if False.\n", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/systems/solvers.py", + "line": 700, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": null, - "is_public": true - }, - { - "name": "estimate_dt", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 3267, - "signature": "def estimate_dt(self):", - "parameters": [], "returns": null, - "existing_docstring": "\n Calculates an appropriate advective timestep for the given\n mesh and velocity configuration.\n", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "F0", + "name": "DuDt", "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 136, + "file": "src/underworld3/systems/solvers.py", + "line": 710, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Pointwise source/sink term :math:`f_0(u)`.\n\nThis represents the volumetric source term in the weak form.", - "harvested_comments": [ - "Note: negative sign for weak form" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "F1", + "name": "DFDt", "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 151, + "file": "src/underworld3/systems/solvers.py", + "line": 714, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Flux term :math:`\\mathbf{F}_1(u, \\nabla u)`.\n\nThis represents the flux that appears in the divergence term.", - "harvested_comments": [ - "Default: simple diffusion" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "my_equation_problem_description", + "name": "water_content", "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 172, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Set up the problem description for the PETSc solver.\n\nThis method defines the residual terms for the finite element formulation.", - "harvested_comments": [ - "Source term (f0 - pointwise integration)", - "Flux term (f1 - integration by parts)" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/systems/solvers.py", + "line": 985, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SNES_MyEquation", - "is_public": true - }, - { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 188, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Source term function.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_Richards", "is_public": true }, { - "name": "f", + "name": "capacity", "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 193, + "file": "src/underworld3/systems/solvers.py", + "line": 999, "signature": "(self, value)", "parameters": [ { @@ -2782,37 +2864,46 @@ } ], "returns": null, - "existing_docstring": "Set the source term.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_Richards", "is_public": true }, { - "name": "alpha", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 199, - "signature": "(self)", - "parameters": [], + "name": "tolerance", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2233, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Example parameter alpha.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "alpha", + "name": "saddle_preconditioner", "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 204, + "file": "src/underworld3/systems/solvers.py", + "line": 2339, "signature": "(self, value)", "parameters": [ { @@ -2823,37 +2914,46 @@ } ], "returns": null, - "existing_docstring": "Set parameter alpha.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "beta", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 210, - "signature": "(self)", - "parameters": [], + "name": "smoothing", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3386, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Example parameter beta.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_MultiComponent_Projection", "is_public": true }, { - "name": "beta", + "name": "uw_weighting_function", "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 215, + "file": "src/underworld3/systems/solvers.py", + "line": 3445, "signature": "(self, value)", "parameters": [ { @@ -2864,2073 +2964,1692 @@ } ], "returns": null, - "existing_docstring": "Set parameter beta.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "SNES_MultiComponent_Projection", "is_public": true }, { - "name": "constitutive_model", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 221, - "signature": "(self)", + "name": "TestCreateMetric", + "kind": "class", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 28, + "signature": "class TestCreateMetric", "parameters": [], "returns": null, - "existing_docstring": "Constitutive model defining material response.", + "existing_docstring": "Tests for create_metric function.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": null, "is_public": true }, { - "name": "constitutive_model", - "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 226, - "signature": "(self, model)", - "parameters": [ - { - "name": "model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "TestMetricFromGradient", + "kind": "class", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 58, + "signature": "class TestMetricFromGradient", + "parameters": [], "returns": null, - "existing_docstring": "Set the constitutive model.", + "existing_docstring": "Tests for metric_from_gradient function.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": null, "is_public": true }, { - "name": "CM_is_setup", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 232, - "signature": "(self)", + "name": "TestMetricFromField", + "kind": "class", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 117, + "signature": "class TestMetricFromField", "parameters": [], "returns": null, - "existing_docstring": "Check if constitutive model is set up.", - "harvested_comments": [ - "No model needed" - ], + "existing_docstring": "Tests for metric_from_field function.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", - "is_public": true - }, - { - "name": "solve", - "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 240, - "signature": "(self, verbose = False, debug = False)", - "parameters": [ - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "debug", - "type_hint": null, - "default": "False", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Solve the equation system.\n\nParameters\n----------\nverbose : bool\n Enable verbose solver output\ndebug : bool\n Enable debug output\n\nReturns\n-------\nConvergence information from PETSc solver\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.", - "harvested_comments": [ - "Set up problem if needed", - "Call parent solve method" - ], - "status": "complete", - "needs": [], - "parent_class": "SNES_MyEquation", + "parent_class": null, "is_public": true }, { - "name": "estimate_dt", - "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 266, - "signature": "(self, dt_min = 1e-15, dt_max = 1.0)", - "parameters": [ - { - "name": "dt_min", - "type_hint": null, - "default": "1e-15", - "description": "" - }, - { - "name": "dt_max", - "type_hint": null, - "default": "1.0", - "description": "" - } - ], + "name": "UnitsError", + "kind": "class", + "file": "src/underworld3/units.py", + "line": 25, + "signature": "class UnitsError", + "parameters": [], "returns": null, - "existing_docstring": "Estimate appropriate timestep for stability.\n\nParameters\n----------\ndt_min : float\n Minimum allowed timestep\ndt_max : float\n Maximum allowed timestep\n\nReturns\n-------\nfloat\n Estimated timestep", - "harvested_comments": [ - "Get characteristic length scale", - "Example: CFL-type condition", - "dt < h^2 / (2 * diffusivity) for diffusion", - "Extract diffusivity parameter (problem-specific)", - "Additional stability constraints" + "existing_docstring": "Exception raised for units-related errors.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "SNES_MyEquation", + "parent_class": null, "is_public": true }, { - "name": "F0", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 354, - "signature": "(self)", + "name": "DimensionalityError", + "kind": "class", + "file": "src/underworld3/units.py", + "line": 31, + "signature": "class DimensionalityError", "parameters": [], "returns": null, - "existing_docstring": "Vector pointwise source term.", + "existing_docstring": "Exception raised for dimensional inconsistency errors.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyVectorEquation", + "parent_class": null, "is_public": true }, { - "name": "F1", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 364, - "signature": "(self)", + "name": "NoUnitsError", + "kind": "class", + "file": "src/underworld3/units.py", + "line": 37, + "signature": "class NoUnitsError", "parameters": [], "returns": null, - "existing_docstring": "Vector flux term - typically a tensor.", - "harvested_comments": [ - "Default: identity tensor (for demonstration)" - ], + "existing_docstring": "Exception raised when units are expected but not found.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyVectorEquation", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solver_template.py", - "line": 377, - "signature": "(self)", + "name": "Stateful", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 1, + "signature": "class Stateful", "parameters": [], "returns": null, - "existing_docstring": "Vector source term.", + "existing_docstring": "This is a mixin class for underworld objects that are stateful.\nThe state of an object is incremented whenever it is modified.\nFor example, heavy variables have states, and when a user modifies\nit within its `access()` context manager, its state is incremented\nat the conclusion of their modifications.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_MyVectorEquation", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 382, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "SymbolicProperty", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 38, + "signature": "class SymbolicProperty", + "parameters": [], "returns": null, - "existing_docstring": "Set vector source term.", + "existing_docstring": "Property descriptor that automatically unwraps symbolic objects.\n\nThis descriptor provides a centralized way to handle symbolic objects in setters\nthroughout Underworld3. It automatically unwraps objects that implement the\n_sympify_() protocol, eliminating the need for users to access `.sym` properties.\n\nParameters\n----------\nattr_name : str, optional\n The attribute name to store the value. If not provided, will be set\n automatically using __set_name__.\nmatrix_wrap : bool, default False\n If True, automatically wraps scalar values in sympy.Matrix([value])\nallow_none : bool, default True\n If False, raises ValueError when attempting to set to None\ndoc : str, optional\n Docstring for the property\n\nExamples\n--------\nUsage in a solver class::\n\n class MySolver:\n # Simple usage - auto-unwraps symbolic objects\n uw_function = SymbolicProperty()\n\n # With Matrix wrapping for solver compatibility\n source_term = SymbolicProperty(matrix_wrap=True)\n\n # Disallow None values\n required_field = SymbolicProperty(allow_none=False)\n\nNotes\n-----\nObjects implementing _sympify_() include:\n- UWexpression\n- UnitAwareDerivativeMatrix (from temperature.diff(y))\n- MeshVariable and SwarmVariable\n- Any custom object with _sympify_() method", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_MyVectorEquation", + "parent_class": null, "is_public": true }, { - "name": "poisson_problem_description", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 234, - "signature": "(self)", + "name": "ExpressionDescriptor", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 132, + "signature": "class ExpressionDescriptor", "parameters": [], "returns": null, - "existing_docstring": "Build residual terms for Poisson FEM assembly.", - "harvested_comments": [ - "f1 residual term (weighted integration) - scalar function", - "f1 residual term (integration by parts / gradients)" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SNES_Poisson", + "existing_docstring": "Unified descriptor for persistent UWexpression containers.\n\nCreates UWexpression objects ONCE and preserves their identity. The expression's\n.sym content references other expressions for lazy evaluation.\n\nUsed for:\n- Solver templates (F0, F1, PF0) - read-only computed expressions\n- Solver parameters (bodyforce, penalty) - user-settable expressions\n- Constitutive model parameters (viscosity, diffusivity) - user-settable\n- Computed properties (stress, flux) - read-only\n\nParameters\n----------\nname : str or callable\n LaTeX name for the expression. If callable, called with (obj) to get name.\nvalue_fn : callable\n Function that returns the initial symbolic value. Called ONCE when created.\n Should return references to other expressions for lazy evaluation.\ndescription : str\n Description of the expression for documentation\nread_only : bool, default False\n If True, prevents user from setting .sym (for templates/computed properties)\nunits : str, optional\n Expected units for validation (e.g., \"Pa*s\", \"m/s\")\nvalidator : callable, optional\n Custom validation function called when value is set\ncategory : str, optional\n Category for introspection: \"parameter\", \"template\", \"computed\"\nattr_name : str, optional\n Attribute name to store the expression. Auto-generated if not provided.\n\nExamples\n--------\nDefine parameter and template expressions in a solver class::\n\n class MySolver:\n # Parameter (user can change)\n bodyforce = ExpressionDescriptor(\n r\"\\mathbf{f}\",\n lambda self: sympy.Matrix([[0] * self.mesh.dim]),\n \"Body force\",\n read_only=False,\n category=\"parameter\"\n )\n\n # Template (read-only, references parameter)\n F0 = ExpressionDescriptor(\n r\"f_0\",\n lambda self: -self.bodyforce, # References bodyforce expression!\n \"Force term\",\n read_only=True,\n category=\"template\"\n )\n\nNotes\n-----\n- Expression container created ONCE on first access\n- Object identity preserved (same Python id)\n- value_fn evaluated ONCE to set .sym with expression references\n- Lazy evaluation happens automatically through expression references\n- For parameters: user can update .sym content\n- For templates: .sym is immutable, contains expression references", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 246, - "signature": "(self)", + "name": "Parameter", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 334, + "signature": "class Parameter", "parameters": [], "returns": null, - "existing_docstring": "Source term for the Poisson equation.\n\nThe source term :math:`f` appears on the right-hand side:\n\n.. math::\n \\nabla \\cdot (\\kappa \\nabla u) = f\n\nReturns\n-------\nsympy.Matrix\n Source term expression (scalar, shape ``(1, 1)``).\n\nSee Also\n--------\nconstitutive_model : Provides the diffusivity :math:`\\kappa`.", + "existing_docstring": "Expression descriptor for user-settable parameters.\n\nThin wrapper around ExpressionDescriptor with read_only=False and category=\"parameter\".\n\nExamples\n--------\nDefine a user-settable parameter::\n\n class MySolver:\n bodyforce = Parameter(\n r\"\\mathbf{f}\",\n lambda self: sympy.Matrix([[0] * self.mesh.dim]),\n \"Body force vector\"\n )\n\nParameters can be modified by the user::\n\n solver.bodyforce.sym = new_value", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Poisson", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 266, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "Template", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 369, + "signature": "class Template", + "parameters": [], "returns": null, - "existing_docstring": "Set the source term (handles units and scaling).", - "harvested_comments": [ - "Handle UWQuantity with units - enforce \"units everywhere\" principle", - "Extract the plain value", - "If ND scaling is active, scale the constant", - "The source term should have same dimensionality as the unknown field", - "Access via self.Unknowns.u (Poisson) or self.Unknowns.DuDt.u (Stokes)" - ], - "status": "minimal", + "existing_docstring": "Expression descriptor for read-only template expressions.\n\nThin wrapper around ExpressionDescriptor with read_only=True and category=\"template\".\n\nTemplates contain references to other expressions for lazy evaluation. When the owning\nobject's `is_setup` flag is False (indicating parameters have changed), the template\nautomatically re-evaluates its lambda and updates the expression's symbolic content.\n\nExamples\n--------\nDefine a template that references a parameter::\n\n class MySolver:\n bodyforce = Parameter(r\"\\mathbf{f}\", ..., \"Body force\")\n\n F0 = Template(\n r\"f_0\",\n lambda self: -self.bodyforce, # References bodyforce expression\n \"Force term\"\n )\n\nTemplates are read-only but auto-update when parameters change::\n\n # Cannot set directly\n solver.F0.sym = value # Raises AttributeError\n\n # But when parameters change:\n solver.bodyforce.sym = new_value # Sets is_setup = False\n f0 = solver.F0 # Automatically re-evaluates and updates .sym in-place", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Poisson", + "parent_class": null, "is_public": true }, { - "name": "CM_is_setup", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 328, - "signature": "(self)", + "name": "uw_object", + "kind": "class", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 442, + "signature": "class uw_object", "parameters": [], "returns": null, - "existing_docstring": "Whether the constitutive model is configured for this solver.", + "existing_docstring": "The UW (mixin) class adds common functionality that we wish to provide on all uw_objects\nsuch as the view methods (classmethod for generic information and instance method that can be over-ridden)\nto provide instance-specific information", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Poisson", + "parent_class": null, "is_public": true }, { - "name": "darcy_problem_description", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 453, - "signature": "(self)", + "name": "UW_Pause", + "kind": "class", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 64, + "signature": "class UW_Pause", "parameters": [], "returns": null, - "existing_docstring": "Build residual terms for Darcy flow FEM assembly.", - "harvested_comments": [ - "f1 residual term (weighted integration)", - "f1 residual term (integration by parts / gradients)", - "Flow calculation" - ], - "status": "minimal", + "existing_docstring": "Interactive pause exception for Underworld3 notebooks.\n\nThis exception is raised by uw.pause() in notebook environments.\nIt displays a clean, formatted message without Python tracebacks.\n\nIn non-notebook environments (scripts, HPC), uw.pause() does nothing\nand this exception is never raised.\n\nParameters\n----------\nmessage : str\n The pause message to display\nexplanation : str, optional\n Additional explanation for the user\n\nExamples\n--------\nIn a Jupyter notebook:\n\n>>> raise UW_Pause(\"Mesh visualization complete\")\n\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nUnderworld3 - Paused \ud83d\uded1\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nMesh visualization complete\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nNotes\n-----\nUse uw.pause() instead of raising this directly - it handles\nthe notebook/script detection automatically.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Darcy", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 467, - "signature": "(self)", + "name": "JITCallbackSet", + "kind": "class", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 194, + "signature": "class JITCallbackSet", "parameters": [], "returns": null, - "existing_docstring": "Source term for the Darcy equation.", + "existing_docstring": "Immutable container for the five PETSc pointwise callback lists.\n\nEach slot holds a tuple of SymPy expressions for one callback role.\nThe structured representation ensures that cache keys preserve which\nrole each expression belongs to, preventing the collision bug where\n``Integral(fn=1)`` and ``BdIntegral(fn=1)`` would share a cached module.\n\nParameters\n----------\nresidual : tuple\n Volume residual expressions (F0, F1 for each field).\nbcs : tuple\n Essential boundary condition expressions.\njacobian : tuple\n Jacobian expressions (G0, G1, G2, G3 for each field pair).\nbd_residual : tuple\n Boundary residual expressions (includes ``petsc_n[]`` access).\nbd_jacobian : tuple\n Boundary Jacobian expressions.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Darcy", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 472, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "ParamType", + "kind": "class", + "file": "src/underworld3/utilities/_params.py", + "line": 51, + "signature": "class ParamType", + "parameters": [], "returns": null, - "existing_docstring": "Set the source term.", + "existing_docstring": "Supported parameter types for uw.Param.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Darcy", + "parent_class": null, "is_public": true }, { - "name": "darcy_flux", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 478, - "signature": "(self)", + "name": "Param", + "kind": "class", + "file": "src/underworld3/utilities/_params.py", + "line": 62, + "signature": "class Param", "parameters": [], "returns": null, - "existing_docstring": "Darcy flux velocity computed from pressure gradient.", + "existing_docstring": "Rich parameter definition with type, units, and validation.\n\nFor QUANTITY type, dimension is automatically derived from units:\n - units=\"km\" \u2192 dimension is [length]\n - units=\"Pa*s\" \u2192 dimension is [mass]/[length]/[time]\n\nThis prevents any clash between units and dimension.\n\nAttributes:\n value: Default value (numeric fallback)\n type: Explicit type (auto-detected if None)\n units: Units string (e.g., \"km\", \"Pa*s\")\n bounds: (min, max) tuple for validation after unit conversion\n description: Help text for CLI output\n\nExample:\n >>> uw.Param(0.5, units=\"km\", bounds=(0.01, 100), description=\"Cell size\")", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Darcy", + "parent_class": null, "is_public": true }, { - "name": "v", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 484, - "signature": "(self)", + "name": "Params", + "kind": "class", + "file": "src/underworld3/utilities/_params.py", + "line": 116, + "signature": "class Params", "parameters": [], "returns": null, - "existing_docstring": "Projected Darcy velocity field.", + "existing_docstring": "Parameter container with PETSc command-line override support.\n\nParameters are defined as keyword arguments with default values.\nEach parameter can be overridden from the command line using\nthe same name as a PETSc option flag.\n\nNaming Convention:\n Use 'uw_' prefix for parameter names (e.g., uw_mesh_resolution).\n The CLI flag then matches: -uw_mesh_resolution 0.025\n\nNote:\n PETSc strips the 'uw_' prefix when storing options internally.\n This class handles that automatically - you use uw_name in Python\n and -uw_name on the command line; lookup is handled transparently.\n\nAttributes:\n _defaults: Original default values\n _sources: Where each value came from ('default', 'cli', 'override')\n\nExample:\n >>> params = Params(uw_resolution=0.1, uw_viscosity=1e21)\n >>> params.uw_resolution # Returns 0.1 or CLI override\n 0.1\n >>> params.uw_resolution = 0.05 # Notebook override\n >>> params.uw_resolution\n 0.05", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SNES_Darcy", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "v", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 489, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "CaptureStdout", + "kind": "class", + "file": "src/underworld3/utilities/_utils.py", + "line": 131, + "signature": "class CaptureStdout", + "parameters": [], "returns": null, - "existing_docstring": "Set the velocity projection target.", + "existing_docstring": "Captures stdout (e.g., from ``print()``) as a variable.\n\nBased on ``contextlib.redirect_stdout``, but saves the user the trouble of\ndefining and reading from an IO stream. Useful for testing the output of functions\nthat are supposed to print certain output.\n\nCitation: https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Darcy", + "parent_class": null, "is_public": true }, { - "name": "solve", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 495, - "signature": "(self, zero_init_guess: bool = True, timestep: float = None, verbose: bool = False, _force_setup: bool = False)", - "parameters": [ - { - "name": "zero_init_guess", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "timestep", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "verbose", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "_force_setup", - "type_hint": "bool", - "default": "False", - "description": "" - } - ], + "name": "CustomMGHierarchy", + "kind": "class", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 509, + "signature": "class CustomMGHierarchy", + "parameters": [], "returns": null, - "existing_docstring": "Solve the Darcy flow system.\n\nComputes the pressure field and Darcy flux velocity.\n\nParameters\n----------\nzero_init_guess : bool, optional\n If True (default), start from zero initial guess.\n If False, use current field values as initial guess.\ntimestep : float, optional\n Timestep value for inertial terms (if applicable).\nverbose : bool, optional\n If True, print solver progress information.\n_force_setup : bool, optional\n Force re-setup of solver even if already configured.\n\nNotes\n-----\nAfter solving, the pressure field ``self.u`` and velocity field\n``self.v`` contain the solution.", - "harvested_comments": [ - "Solve pressure", - "Now solve flow field", - "self._v_projector.petsc_options[\"snes_rtol\"] = 1.0e-6", - "self._v_projector.petsc_options.delValue(\"ksp_monitor\")" - ], + "existing_docstring": "A generalized FMG hierarchy: a sequence of level meshes (coarsest..finest)\nwhose transfers are built by a pluggable builder, with BCs applied at every\nlevel. Adapter-agnostic \u2014 it consumes meshes + a way to get each level's\nBC-reduced DOF map; it does not know how the levels were produced.\n\nParameters\n----------\nlevel_meshes : list of Mesh\n Coarsest-first; the LAST entry must be the solver's own mesh.\nbuilder : {\"barycentric\", \"rbf\"}\n Per-level node prolongation builder.\nfield_id : int or None\n Field index for multi-field solvers (e.g. 0 = velocity); None = single field.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "SNES_Darcy", + "parent_class": null, "is_public": true }, { - "name": "stokes_problem_description", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 733, - "signature": "(self)", + "name": "Status", + "kind": "class", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 24, + "signature": "class Status", "parameters": [], "returns": null, - "existing_docstring": "Build residual terms for Stokes FEM assembly (deprecated).", - "harvested_comments": [ - "f0 residual term", - "f1 residual term", - "p0 residual term" - ], + "existing_docstring": "Diagnostic status levels.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "CM_is_setup", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 747, - "signature": "(self)", + "name": "DiagnosticResult", + "kind": "class", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 33, + "signature": "class DiagnosticResult", "parameters": [], "returns": null, - "existing_docstring": "Whether the constitutive model is configured for this solver.", + "existing_docstring": "Result of a single diagnostic check.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "strainrate", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 752, - "signature": "(self)", + "name": "DimensionalityMixin", + "kind": "class", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 13, + "signature": "class DimensionalityMixin", "parameters": [], "returns": null, - "existing_docstring": "Symmetric strain rate tensor from velocity gradients.\n\nThe strain rate tensor :math:`\\dot{\\varepsilon}` is computed as:\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)\n\nReturns\n-------\nsympy.Matrix\n Symmetric tensor of shape ``(dim, dim)`` where ``dim`` is the\n mesh dimensionality.\n\nSee Also\n--------\nstrainrate_1d : Voigt notation (vector) form.\nstress_deviator : Deviatoric stress computed from strain rate.", + "existing_docstring": "Mixin to add dimensionality tracking and non-dimensionalization capability\nto any class that has units.\n\nAttributes:\n _scaling_coefficient: Reference scale for non-dimensionalization\n _is_nondimensional: Whether currently in non-dimensional state\n _original_units: Stores units before non-dimensionalization", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "strainrate_1d", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 775, - "signature": "(self)", + "name": "NonDimensionalView", + "kind": "class", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 152, + "signature": "class NonDimensionalView", "parameters": [], "returns": null, - "existing_docstring": "Strain rate in Voigt notation (vector form).\n\nConverts the symmetric strain rate tensor to Voigt notation for\nuse in constitutive model calculations. In 2D, returns a 3-component\nvector; in 3D, a 6-component vector.\n\nReturns\n-------\nsympy.Matrix\n Strain rate in Voigt notation.\n\nSee Also\n--------\nstrainrate : Full tensor form.", + "existing_docstring": "A view of a variable in non-dimensional form.\nThis allows accessing arrays in non-dimensional form without modifying the original.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "strainrate_star_1d", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 794, - "signature": "(self)", + "name": "DocstringFormat", + "kind": "class", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 15, + "signature": "class DocstringFormat", "parameters": [], "returns": null, - "existing_docstring": "Historical strain rate in Voigt notation (for viscoelastic models).\n\nUsed in viscoelastic formulations where the stress depends on\nboth current and historical strain rates.\n\nReturns\n-------\nsympy.Matrix\n Historical strain rate in Voigt notation.\n\nSee Also\n--------\nstrainrate_1d : Current strain rate.", + "existing_docstring": "Detected docstring format.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "stress_deviator", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 812, - "signature": "(self)", + "name": "ParsedDocstring", + "kind": "class", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 23, + "signature": "class ParsedDocstring", "parameters": [], "returns": null, - "existing_docstring": "Deviatoric stress tensor from the constitutive model.\n\nThe deviatoric stress :math:`\\boldsymbol{\\tau}` is the traceless part\nof the stress tensor, computed by the constitutive model from the\nstrain rate:\n\n.. math::\n \\boldsymbol{\\tau} = \\boldsymbol{\\eta} : \\dot{\\boldsymbol{\\varepsilon}}\n\nFor a Newtonian fluid: :math:`\\boldsymbol{\\tau} = 2\\eta\\dot{\\boldsymbol{\\varepsilon}}`\n\nReturns\n-------\nsympy.Matrix\n Deviatoric stress tensor of shape ``(dim, dim)``.\n\nSee Also\n--------\nstress : Total stress (deviatoric + pressure).\nconstitutive_model : Provides the viscosity relationship.", + "existing_docstring": "Parsed docstring components.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "stress_deviator_1d", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 837, - "signature": "(self)", + "name": "MathematicalMixin", + "kind": "class", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 18, + "signature": "class MathematicalMixin", "parameters": [], "returns": null, - "existing_docstring": "Deviatoric stress in Voigt notation.\n\nReturns\n-------\nsympy.Matrix\n Deviatoric stress in Voigt notation.\n\nSee Also\n--------\nstress_deviator : Full tensor form.", + "existing_docstring": "Fixed Mathematical Mixin with consistent error handling and operation support.\n\nKey improvements:\n- Better error handling for unsupported operations\n- Intelligent method delegation with signature handling\n- Proper validation of sym property\n- Consistent behavior across different variable types", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "stress", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 852, - "signature": "(self)", + "name": "UnitAwareDerivativeMatrix", + "kind": "class", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 802, + "signature": "class UnitAwareDerivativeMatrix", "parameters": [], "returns": null, - "existing_docstring": "Total Cauchy stress tensor.\n\nThe total stress combines the deviatoric stress and pressure:\n\n.. math::\n \\boldsymbol{\\sigma} = \\boldsymbol{\\tau} - p\\mathbf{I}\n\nwhere :math:`\\boldsymbol{\\tau}` is the deviatoric stress and\n:math:`p` is the pressure (positive in compression).\n\nReturns\n-------\nsympy.Matrix\n Total stress tensor of shape ``(dim, dim)``.\n\nSee Also\n--------\nstress_deviator : Deviatoric (traceless) part.", + "existing_docstring": "Wrapper for SymPy Matrix derivatives that provides unit-aware indexing.\n\nWhen you index into this matrix (e.g., result[0]), it automatically wraps\nthe element in a unit-aware object using uw.with_units().\n\nWhen used in arithmetic expressions without indexing, it automatically unwraps\nto the underlying SymPy Matrix via the _sympify_() protocol.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "stress_1d", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 875, - "signature": "(self)", + "name": "DelayedCallbackManager", + "kind": "class", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 36, + "signature": "class DelayedCallbackManager", "parameters": [], "returns": null, - "existing_docstring": "Total stress in Voigt notation.\n\nReturns\n-------\nsympy.Matrix\n Total stress in Voigt notation.\n\nSee Also\n--------\nstress : Full tensor form.", + "existing_docstring": "Thread-local manager for delayed callbacks across multiple NDArray_With_Callback instances.\n\nThis allows batch operations across multiple arrays to accumulate callbacks\nand trigger them all at once when the context exits.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "div_u", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 890, - "signature": "(self)", + "name": "NDArray_With_Callback", + "kind": "class", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 106, + "signature": "class NDArray_With_Callback", "parameters": [], "returns": null, - "existing_docstring": "Velocity divergence.\n\nFor incompressible flow, this should be zero:\n\n.. math::\n \\nabla \\cdot \\mathbf{u} = 0\n\nReturns\n-------\nsympy.Expr\n Scalar divergence expression.\n\nNotes\n-----\nNon-zero divergence indicates compressibility or mass sources/sinks.", + "existing_docstring": "A numpy ndarray subclass that triggers callbacks when array data is modified.\n\nThis class maintains full numpy array compatibility while providing reactive\nprogramming capabilities for scientific computing applications.\n\n**Callback Function Signature**::\n\n def callback(array: NDArray_With_Callback, change_info: dict) -> None:\n pass\n\nThe ``change_info`` dictionary contains:\n\n- ``operation`` (str): Operation name ('setitem', 'iadd', 'fill', etc.)\n- ``indices`` (tuple/slice/None): Location of change (for setitem operations)\n- ``old_value`` (array-like/None): Previous values (when available)\n- ``new_value`` (array-like): New values being assigned\n- ``array_shape`` (tuple): Current shape of the array\n- ``array_dtype`` (np.dtype): Data type of the array\n\n**Features**:\n\n- **Multiple callbacks**: ``add_callback()``, ``remove_callback()``, ``clear_callbacks()``\n- **Enable/disable**: ``enable_callbacks()``, ``disable_callbacks()``\n- **Delayed execution**: ``delay_callback()``, ``delay_callbacks_global()``\n- **MPI synchronization**: Automatic barriers in parallel contexts\n- **Weak references**: Owner tracking without circular dependencies\n- **Global reductions**: MPI-aware ``global_max()``, ``global_min()``, ``global_sum()``, etc.\n\n**Global Reduction Operations (MPI-aware)**:\n\n- ``global_max(axis=None)``: Maximum value across all MPI ranks\n- ``global_min(axis=None)``: Minimum value across all MPI ranks\n- ``global_sum(axis=None)``: Sum of all values across all MPI ranks\n- ``global_mean(axis=None)``: True mean (global sum / global count)\n- ``global_size()``: Total number of elements across all ranks\n- ``global_norm(ord=2)``: 2-norm (Euclidean) across all ranks\n- ``global_rms()``: Root mean square across all ranks\n\nThese methods use MPI collective operations (``allreduce``).\nAll ranks must call these methods (they are collective operations).\nSubclasses like ``UnitAwareArray`` override these to preserve units.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "constraints", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 910, - "signature": "(self)", + "name": "UnitAwareArray", + "kind": "class", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 33, + "signature": "class UnitAwareArray", "parameters": [], "returns": null, - "existing_docstring": "Constraint equation for the saddle-point system.\n\nBy default, this is the incompressibility constraint\n:math:`\\nabla \\cdot \\mathbf{u} = 0`. Can be modified for\ncompressible or other constrained formulations.\n\nReturns\n-------\nsympy.Expr\n Constraint expression.", + "existing_docstring": "A numpy ndarray subclass that combines callback functionality with unit awareness.\n\nExtends ``NDArray_With_Callback`` to provide automatic unit tracking,\ncompatibility checking, and integration with the UW3 unit conversion system.\n\nOperations preserve dimensional consistency: compatible units are added,\nincompatible units raise errors, and multiplication combines units.\n\n**Global Reduction Operations (MPI-aware, unit-preserving)**:\n\n- ``global_max()`` -> UWQuantity (same units as array)\n- ``global_min()`` -> UWQuantity (same units as array)\n- ``global_sum()`` -> UWQuantity (same units as array)\n- ``global_mean()`` -> UWQuantity (same units as array)\n- ``global_size()`` -> int (count, no units)\n- ``global_norm()`` -> UWQuantity (same units as array)\n- ``global_rms()`` -> UWQuantity (same units as array)\n- ``global_var()`` -> UWQuantity (units squared)\n- ``global_std()`` -> UWQuantity (same units as array)\n\nTensor arrays (ndim > 2) raise NotImplementedError for global reductions.\nUse component-wise operations or slice the array for tensors.\n\nInherits the callback mechanism from ``NDArray_With_Callback``, enabling it\nto work with any storage backend (PETSc, pyvista, etc.) by changing only\nthe callback. Provides consistent ``.units``, ``.magnitude``, and unit-aware\narithmetic regardless of underlying storage.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "constraints", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 925, - "signature": "(self, constraints_matrix)", - "parameters": [ - { - "name": "constraints_matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "UnitAwareBaseScalar", + "kind": "class", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 13, + "signature": "class UnitAwareBaseScalar", + "parameters": [], "returns": null, - "existing_docstring": "Set the constraint equation (e.g., incompressibility).", + "existing_docstring": "A BaseScalar subclass that carries units information.\n\nThis class maintains full compatibility with SymPy's vector system and\nUnderworld's JIT compilation while adding unit awareness. The JIT system\ndetects coordinates by looking for BaseScalar atoms, so by inheriting from\nBaseScalar, these unit-aware coordinates will be properly recognized and\ncompiled.\n\nAttributes:\n _units: The units of this coordinate (e.g., \"meter\", \"kilometer\")\n _ccodestr: The C code string for JIT compilation (set by mesh initialization)", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "bodyforce", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 932, - "signature": "(self)", + "name": "TimeSymbol", + "kind": "class", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 86, + "signature": "class TimeSymbol", "parameters": [], "returns": null, - "existing_docstring": "Body force vector (source term).\n\nThe volumetric body force :math:`\\mathbf{f}` appears on the\nright-hand side of the momentum equation:\n\n.. math::\n -\\nabla \\cdot \\boldsymbol{\\sigma} = \\mathbf{f}\n\nCommon examples include gravity (:math:`\\rho\\mathbf{g}`) or\nbuoyancy forces.\n\nReturns\n-------\nUWexpression\n Body force vector expression.", + "existing_docstring": "A sympy Symbol subclass that carries _ccodestr and _units for JIT.\n\nStandard sympy Symbols are immutable and don't allow setting arbitrary\nattributes. This subclass permits _ccodestr (for C code generation)\nand _units (for dimensional analysis), following the same pattern as\nUnitAwareBaseScalar for spatial coordinates.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "bodyforce", + "name": "consistent_jacobian", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 952, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 142, + "signature": "def consistent_jacobian(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the body force vector (e.g., gravity, buoyancy).", - "harvested_comments": [ - "Convert UWQuantity objects to SymPy expressions before Matrix creation", - "If UWQuantity contains a Matrix, extract the scalar element" - ], - "status": "minimal", + "existing_docstring": "Jacobian tangent selection: ``False`` | ``True`` | ``\"continuation\"``.\n\n Selects the tangent used by :meth:`_jacobian_source` and the solve\n dispatch; the residual is never affected, so the converged solution\n always satisfies the exact constitutive law.\n\n ``False`` (default)\n Differentiate the residual flux *as wrapped* \u2014 the effective\n viscosity is frozen, giving a Picard / defect-correction tangent.\n Bit-identical to the long-standing behaviour. Globally robust;\n load-bearing for the tuned hard-yield viscoplastic paths.\n ``True``\n Unwrap the flux before differentiation so the tangent captures\n :math:`\\partial\\eta/\\partial(\\nabla v)` (full Newton). Fast near\n the solution; its yield kink can stall the line search far from it.\n ``\"continuation\"``\n Picard :math:`\\rightarrow` Newton. Blend\n :math:`J(\\alpha) = J_{\\mathrm{picard}} + \\alpha\\,(J_{\\mathrm{newton}}\n - J_{\\mathrm{picard}})` with :math:`\\alpha` a ``constants[]``\n parameter ramped :math:`0 \\rightarrow 1` by a SNES monitor as the\n residual drops. Picard locates the basin, Newton gives quadratic\n convergence inside it (cf. Spiegelman et al. 2016; ASPECT\n defect-correction-then-Newton). :math:`\\alpha = 0` is bit-identical\n to Picard, so no recompile is needed to switch.\n\n The Newton flux for a model whose flux has a non-smooth yield kink is\n the model's own smooth law (``constitutive_model.flux_jacobian``) when\n it provides one; otherwise the exact unwrapped flux. See\n ``docs/developer/design/jacobian-unwrap-constants-bug.md``.\n\n Raises\n ------\n ValueError\n On assignment of anything other than ``False``, ``True`` or\n ``\"continuation\"``. Falsy values (``None``, ``0``, ``\"\"``)\n normalize to ``False`` (they already selected the Picard tangent).\n Before validation, any other truthy value (``1``, ``\"picard\"``,\n ``\"Continuation\"``) silently selected the full-Newton tangent.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "saddle_preconditioner", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 973, - "signature": "(self)", + "name": "set_custom_mg", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 294, + "signature": "def set_custom_mg(self, coarse_meshes, kind=\"barycentric\", verbose=False):", "parameters": [], "returns": null, - "existing_docstring": "Preconditioner for the Schur complement in the saddle-point system.\n\nFor the Stokes system, the default preconditioner is :math:`1/\\eta`\n(inverse viscosity), which approximates the Schur complement\n:math:`\\mathbf{S} \\approx \\mathbf{B}\\mathbf{A}^{-1}\\mathbf{B}^T`.\n\nReturns\n-------\nsympy.Expr\n Preconditioner expression (typically inverse viscosity).\n\nNotes\n-----\nA good preconditioner significantly improves convergence of the\niterative solver. For variable viscosity, use the local viscosity.", + "existing_docstring": "Drive geometric multigrid with a prolongation we build ourselves.\n\n Supplies a sequence of (possibly **non-nested**) coarse meshes from which\n a barycentric or RBF prolongation ``P`` is assembled and installed into\n the PCMG via ``PC.setMGInterpolation``; coarse operators are formed by\n Galerkin RAP. This decouples geometric multigrid from a nested\n ``refine()`` hierarchy \u2014 it works even when the solver mesh has no\n refinement hierarchy at all.\n\n Parameters\n ----------\n coarse_meshes : list of Mesh\n Coarsest-first list of coarse meshes (the finest level is the\n solver's own mesh). Need not be nested with the solver mesh.\n kind : {\"barycentric\", \"rbf\"}\n Prolongation builder. ``barycentric`` is FE-exact; ``rbf`` is a\n polyharmonic RBF (Shepard-normalised). Default ``barycentric``.\n verbose : bool\n Print the per-level DOF counts at injection.\n\n Notes\n -----\n Supported both on single-field (scalar / vector) solvers \u2014 where the\n prolongation is installed directly on the solver's ``PCMG`` \u2014 and on the\n **Stokes velocity block** (the ``fieldsplit_velocity_`` sub-PC), where the\n velocity sub-PC is only reachable once the monolithic Jacobian has been\n assembled; the install there assembles the Jacobian, descends the\n fieldsplit to the velocity sub-PC, and rebuilds it as a fresh ``PCMG``\n driven by our ``P``. Injection happens at solve time (after\n ``setFromOptions`` / nullspace attach) via\n :func:`underworld3.utilities.custom_mg.inject_custom_mg`. See\n :mod:`underworld3.utilities.custom_mg`.\n\n .. deprecated:: 2026-07\n This is the legacy **serial-only, finest-only-reduction,\n single-field** path; the Stokes velocity-block support described\n above is delivered by :meth:`set_custom_fmg`, which is the\n canonical entry point (parallel-capable, BC-per-level reduction).\n", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "saddle_preconditioner", + "name": "set_custom_fmg", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 993, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 348, + "signature": "def set_custom_fmg(self, coarse_meshes, *, builder=\"barycentric\",\n field_id=None, verbose=False):", + "parameters": [], "returns": null, - "existing_docstring": "Set the Schur complement preconditioner.", + "existing_docstring": "Drive geometric multigrid with a prolongation built from\n ``coarse_meshes`` \u2014 the canonical custom-MG entry point.\n\n Registers a BC-per-level reduced hierarchy on the solver so the next\n :meth:`solve` builds and installs it (build-time injection). Works in\n parallel and on the **Stokes velocity block** (pass ``field_id=0`` on a\n saddle-point solver). This supersedes the legacy\n :meth:`set_custom_mg` path (serial-only, finest-only reduction,\n single-field).\n\n Parameters\n ----------\n coarse_meshes : list of Mesh\n Coarsest-first list of coarse meshes (the finest level is the\n solver's own mesh). They need only carry the same boundary labels\n as the solver's mesh.\n builder : {\"barycentric\", \"rbf\"}, default \"barycentric\"\n Prolongation builder. ``barycentric`` is FE-exact; ``rbf`` is a\n polyharmonic RBF (Shepard-normalised).\n field_id : int, optional\n Target field for a multi-field (saddle-point) solver; ``0`` is the\n Stokes velocity block. ``None`` (default) for single-field solvers.\n verbose : bool, default False\n Print the per-level DOF counts at injection.\n\n See Also\n --------\n underworld3.utilities.custom_mg.set_custom_fmg : the implementation.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "penalty", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1000, - "signature": "(self)", + "name": "add_update_callback", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 383, + "signature": "def add_update_callback(self, callback):", "parameters": [], "returns": null, - "existing_docstring": "Augmented Lagrangian penalty parameter.\n\nThe penalty :math:`\\lambda` adds a term to the weak form that\npenalizes non-zero divergence:\n\n.. math::\n \\lambda \\int (\\nabla \\cdot \\mathbf{u})(\\nabla \\cdot \\mathbf{v}) \\, dV\n\nThis improves convergence for incompressible flow without\nchanging the solution (since :math:`\\nabla \\cdot \\mathbf{u} = 0`\nat convergence).\n\nReturns\n-------\nUWexpression\n Penalty parameter (typically a large constant, e.g., ``1e6 * eta``).\n\nNotes\n-----\nSet to zero for standard Stokes without augmentation.\nTypical values are ``O(10^6)`` times the characteristic viscosity.", + "existing_docstring": "Register a callback fired at the start of every nonlinear (SNES) iteration.\n\n The callback is invoked as ``callback(solver, iteration)``. Immediately\n before the call the current Newton iterate is scattered into the solver's\n field MeshVariables (so the callback can read ``v``, ``p``, ... at the\n current iterate); immediately afterwards the (possibly modified) fields\n are gathered back into the iterate. Typical uses:\n\n - re-fire an auxiliary solve each iteration \u2014 e.g. a Helmholtz/Projection\n smoother that supplies a regularised field the residual depends on\n (gradient-plasticity / shear-band stabilisation);\n - impose a gauge consistently inside the nonlinear solve \u2014 e.g. remove the\n mean pressure on a surface so the pressure null space is pinned\n (see :meth:`set_pressure_gauge` on the Stokes solver).\n\n Callbacks run in registration order. Registering one forces a re-setup so\n the PETSc ``SNESSetUpdate`` hook is attached. With no callbacks registered\n no hook is installed and the solve path is byte-for-byte unchanged.\n", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "penalty", + "name": "preconditioner", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1026, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 471, + "signature": "def preconditioner(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the augmented Lagrangian penalty parameter.", + "existing_docstring": "Preconditioner selection for the (velocity) block.\n\n One of:\n\n - ``\"auto\"`` (default) \u2014 use geometric Full Multigrid (FMG) when the\n mesh carries a genuine refinement hierarchy\n (``len(mesh.dm_hierarchy) > 1``, i.e. built with ``refinement >= 1``),\n otherwise fall back to algebraic multigrid (GAMG).\n - ``\"fmg\"`` (alias ``\"mg\"``) \u2014 force geometric multigrid. Requires a\n refinement hierarchy; warns and falls back to GAMG if none exists.\n - ``\"gamg\"`` \u2014 force algebraic multigrid (the historical default).\n\n Geometric multigrid is inherently robust to mesh anisotropy (it is built\n from the geometric refinement hierarchy, not the operator connection\n graph), which makes it the preferred choice on adapted/deformed meshes.\n The hierarchy survives the coordinate-deforming adaptation movers and\n only collapses under a true remesh \u2014 in which case ``\"auto\"``\n transparently reverts to GAMG.\n\n For Stokes this governs the velocity fieldsplit block; for scalar/vector\n solvers it governs the top-level preconditioner. Other solvers ignore it.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "estimate_dt", + "name": "is_setup", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1041, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 732, + "signature": "def is_setup(self):", "parameters": [], "returns": null, - "existing_docstring": "Calculates an appropriate advective timestep for the Stokes solver.\n\nThe Stokes equations are quasi-static (no time derivative \u2202v/\u2202t),\nso there is no diffusive CFL constraint. The only relevant timescale\nis the advective one: how long it takes material to cross an element.\n\nThis method computes a per-element timestep:\n dt_i = h_i / |v_i|\n\nwhere h_i is the element radius and v_i is the velocity at the element\ncentroid, then returns the global minimum. This is more accurate than\nusing global max velocity with global min element size, especially for\nnon-uniform meshes with spatially varying velocity.\n\nReturns:\n Pint Quantity or float: The advective timestep with physical time units\n if a model with reference scales is available, otherwise nondimensional.", - "harvested_comments": [ - "Evaluate velocity at element centroids (consistent with AdvDiff)", - "If vel is unit-aware (UnitAwareArray), nondimensionalise it to get", - "consistent nondimensional values that match mesh._radii", - "Note: .magnitude returns physical units, which would be wrong here", - "Plain UWQuantity without units context - use magnitude" - ], + "existing_docstring": "True when the solver's PETSc DM/DS/SNES state is consistent with\n the current settings.\n\n Backed by three fine-grained flags:\n\n - ``_needs_dm_rebuild`` \u2014 mesh or field layout changed\n - ``_needs_bc_reregister`` \u2014 boundary conditions changed\n - ``_needs_function_rewire`` \u2014 only pointwise functions changed\n\n Assigning ``self.is_setup = False`` pessimistically raises all three\n (safe default \u2014 forces full DM teardown on next _build). Assigning\n ``True`` clears all three.\n\n Narrower opt-in: directly assign ``_needs_function_rewire = True`` when\n only F0/F1/Jacobian pointwise functions changed (constitutive model or\n time-derivative manager swap). _build() uses the in-place\n PetscDSSetResidual/SetJacobian path for that case, skipping DM/SNES\n teardown entirely.\n", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Stokes", + "parent_class": null, "is_public": true }, { - "name": "delta_t", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1249, - "signature": "(self)", + "name": "u", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 811, + "signature": "def u(inner_self):", "parameters": [], "returns": null, - "existing_docstring": "Elastic timestep from the constitutive model.", + "existing_docstring": "Primary unknown variable (MeshVariable) being solved for.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_VE_Stokes", + "parent_class": null, "is_public": true }, { - "name": "solve", + "name": "DuDt", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1256, - "signature": "(self, zero_init_guess: bool = True, timestep: float = None, _force_setup: bool = False, verbose = False, evalf = False, order = None)", - "parameters": [ - { - "name": "zero_init_guess", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "timestep", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "_force_setup", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "order", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 833, + "signature": "def DuDt(inner_self):", + "parameters": [], "returns": null, - "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.", - "harvested_comments": [ - "this will force an initialisation because the functions need to be updated", - "Update SemiLagrange Flux terms" - ], - "status": "partial", + "existing_docstring": "Time derivative manager for the unknown variable (advection-diffusion).", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "SNES_VE_Stokes", + "parent_class": null, "is_public": true }, { - "name": "smoothing", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1414, - "signature": "(self)", + "name": "DFDt", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 846, + "signature": "def DFDt(inner_self):", "parameters": [], "returns": null, - "existing_docstring": "Smoothing regularization parameter for the projection.", + "existing_docstring": "Flux time derivative manager (viscoelastic problems).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Projection", + "parent_class": null, "is_public": true }, { - "name": "smoothing", + "name": "E", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1419, - "signature": "(self, smoothing_factor)", - "parameters": [ - { - "name": "smoothing_factor", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 857, + "signature": "def E(inner_self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the smoothing regularization parameter.", + "existing_docstring": "Strain rate tensor: symmetric part of velocity gradient L.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Projection", + "parent_class": null, "is_public": true }, { - "name": "uw_weighting_function", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1425, - "signature": "(self)", + "name": "L", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 862, + "signature": "def L(inner_self):", "parameters": [], "returns": null, - "existing_docstring": "Weighting function applied during projection.", + "existing_docstring": "Velocity gradient tensor: :math:`L_{ij} = \\\\partial u_i / \\\\partial x_j`.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Projection", + "parent_class": null, "is_public": true }, { - "name": "uw_weighting_function", + "name": "W", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1430, - "signature": "(self, user_uw_function)", - "parameters": [ - { - "name": "user_uw_function", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 867, + "signature": "def W(inner_self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the weighting function for the projection.", + "existing_docstring": "Vorticity tensor: antisymmetric part of velocity gradient L.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Projection", + "parent_class": null, "is_public": true }, { - "name": "projection_problem_description", + "name": "Einv2", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1533, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 872, + "signature": "def Einv2(inner_self):", "parameters": [], "returns": null, - "existing_docstring": "Build residual terms for vector projection FEM assembly.", - "harvested_comments": [ - "residual terms - defines the problem:", - "solve for a best fit to the continuous mesh", - "variable given the values in self.function", - "F0 is left in place for the user to inject", - "non-linear constraints if required" - ], + "existing_docstring": "Second invariant of strain rate tensor.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "smoothing", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1557, - "signature": "(self)", + "name": "CoordinateSystem", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 877, + "signature": "def CoordinateSystem(inner_self):", "parameters": [], "returns": null, - "existing_docstring": "Smoothing regularization parameter for the projection.", + "existing_docstring": "Coordinate system of the underlying mesh.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "smoothing", + "name": "get_snes_diagnostics", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1562, - "signature": "(self, smoothing_factor)", - "parameters": [ - { - "name": "smoothing_factor", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 913, + "signature": "def get_snes_diagnostics(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the smoothing regularization parameter.", + "existing_docstring": "\n Extract comprehensive SNES convergence diagnostics with string representations.\n\n Returns:\n --------\n dict\n Comprehensive convergence diagnostics including:\n - converged: bool - Whether solver converged\n - diverged: bool - Whether solver diverged\n - convergence_reason: int - Numerical convergence reason\n - convergence_reason_string: str - Human-readable convergence reason\n - snes_iterations: int - Number of SNES iterations\n - linear_iterations: int - Total number of linear iterations\n - zero_iterations: bool - Whether SNES took zero iterations\n - tolerances: dict - SNES tolerance settings\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "penalty", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1568, - "signature": "(self)", + "name": "check_snes_convergence", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 996, + "signature": "def check_snes_convergence(self, raise_on_divergence=True, print_diagnostics=False):", "parameters": [], "returns": null, - "existing_docstring": "Divergence penalty parameter for incompressibility.", + "existing_docstring": "\n Check SNES convergence and optionally raise exceptions or print diagnostics.\n\n Parameters:\n -----------\n raise_on_divergence : bool\n Whether to raise an exception if solver diverged\n print_diagnostics : bool\n Whether to print diagnostic information\n\n Returns:\n --------\n dict\n SNES diagnostics\n\n Raises:\n -------\n RuntimeError\n If solver diverged and raise_on_divergence=True\n", "harvested_comments": [], - "status": "minimal", + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "solve_with_diagnostics", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1061, + "signature": "def solve_with_diagnostics(self,\n check_convergence=True,\n raise_on_divergence=False,\n print_diagnostics=False,\n **solve_kwargs):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Solve with automatic SNES convergence checking and diagnostics.\n\n Parameters:\n -----------\n check_convergence : bool\n Whether to check convergence after solving\n raise_on_divergence : bool\n Whether to raise exception on divergence\n print_diagnostics : bool\n Whether to print diagnostic information\n **solve_kwargs\n Additional arguments passed to solve()\n\n Returns:\n --------\n dict or None\n SNES diagnostics if check_convergence=True, None otherwise\n\n Raises:\n -------\n RuntimeError\n If solver diverged and raise_on_divergence=True\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "add_condition", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1441, + "signature": "def add_condition(self, f_id, c_type, conds, label, components=None):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Add a dirichlet or neumann condition to the mesh.\n\n This function prepares UW data to use PetscDSAddBoundary().\n\n Parameters\n ----------\n f_id: int\n Index of the solver's field (equation) to apply the condition.\n Note: The solvers field id is usually different to the mesh's field ids.\n c_type: string\n BC type. Either dirichlet (essential) or neumann (natural) conditions.\n conds: array_like of floats or a sympy.Matrix\n eg. For a 3D model with an unconstraint x component: (None, 5, 1.2) or sympy.Matrix([sympy.oo, 5, 1.2])\n label: string\n The label name to apply the BC. To find a label/boundary name run something like\n mesh.view()\n components: array_like, single int value or None.\n (optional) tuple, or int of active conds components to use. Use 'None' for all conds to be used.\n If 'None' and components in 'cond' equal sympy.oo or -sympy.oo those components won't be used.\n eg. For the 3D example cond = (2, 5, 1.2), components = (1,2) the x components is ignored and uncontrainted.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "penalty", + "name": "is_numeric_only", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1573, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1574, + "signature": "def is_numeric_only(val):", + "parameters": [], "returns": null, - "existing_docstring": "Set the divergence penalty parameter.", + "existing_docstring": "Check if value is a pure number (no symbols).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "uw_weighting_function", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1580, - "signature": "(self)", + "name": "add_essential_bc", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1655, + "signature": "def add_essential_bc(self, conds, boundary, components=None):", "parameters": [], "returns": null, - "existing_docstring": "Weighting function applied during projection.", + "existing_docstring": "\n Add an essential (Dirichlet) boundary condition.\n\n Alias for :meth:`add_dirichlet_bc`. Essential BCs constrain the\n solution to specified values at boundary nodes.\n\n Parameters\n ----------\n conds : array-like, float, or sympy.Matrix\n Boundary condition values. Use ``None`` or ``sympy.oo`` for\n unconstrained components.\n boundary : str\n Name of the boundary label (e.g., ``\"Top\"``, ``\"Bottom\"``).\n **Case-sensitive**: must match mesh boundary names exactly.\n components : array-like or None, optional\n Deprecated. Use ``None`` in ``conds`` for unconstrained components.\n\n Examples\n --------\n >>> # Scalar field: fix temperature at boundary\n >>> diffusion.add_essential_bc(300.0, \"Top\")\n\n >>> # Vector field: fix both velocity components\n >>> stokes.add_essential_bc([0.0, 0.0], \"Bottom\")\n\n >>> # Vector field: fix x-component only, leave y free\n >>> stokes.add_essential_bc([0.0, None], \"Left\")\n\n >>> # Symbolic expression as boundary condition\n >>> import sympy\n >>> x, y = stokes.mesh.X\n >>> stokes.add_essential_bc([sympy.sin(x), 0.0], \"Top\")\n\n See Also\n --------\n add_dirichlet_bc : Equivalent method (preferred name).\n add_natural_bc : For flux/traction boundary conditions.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "uw_weighting_function", + "name": "add_natural_bc", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1585, - "signature": "(self, user_uw_function)", - "parameters": [ - { - "name": "user_uw_function", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1698, + "signature": "def add_natural_bc(self, conds, boundary, components=None):", + "parameters": [], "returns": null, - "existing_docstring": "Set the weighting function for the projection.", + "existing_docstring": "\n Add a natural (Neumann) boundary condition.\n\n Natural BCs specify flux or traction at boundaries. These are\n incorporated as surface integrals in the weak form rather than\n direct constraints on the solution.\n\n Parameters\n ----------\n conds : array-like, float, or sympy.Matrix\n Boundary condition values representing flux (scalar problems)\n or traction (vector problems).\n boundary : str\n Name of the boundary label (e.g., ``\"Top\"``, ``\"Bottom\"``).\n **Case-sensitive**: must match mesh boundary names exactly.\n components : array-like or None, optional\n Deprecated. Use ``None`` in ``conds`` for unconstrained components.\n\n Examples\n --------\n >>> # Scalar: specify heat flux at boundary (insulated if 0)\n >>> diffusion.add_natural_bc(0.0, \"Left\") # Insulated boundary\n\n >>> # Scalar: specify inward heat flux\n >>> diffusion.add_natural_bc(100.0, \"Bottom\")\n\n >>> # Vector: apply traction to boundary\n >>> normal = stokes.mesh.CoordinateSystem.unit_e_0\n >>> stokes.add_natural_bc(pressure * normal, \"Right\")\n\n >>> # Free-slip on arbitrary curved surface (spherical models)\n >>> # Uses penalty method with surface normal from mesh.Gamma\n >>> import sympy\n >>> penalty = 1e5\n >>> Gamma = mesh.Gamma # Surface normal vector field\n >>> Gamma_N = Gamma / sympy.sqrt(Gamma.dot(Gamma)) # Normalize\n >>> # Penalize normal velocity component, allow tangential slip\n >>> stokes.add_natural_bc(penalty * Gamma_N.dot(v.sym) * Gamma_N, \"Upper\")\n\n Notes\n -----\n For Stokes problems, natural BCs represent tractions\n :math:`\\\\mathbf{t} = \\\\boldsymbol{\\\\sigma} \\\\cdot \\\\mathbf{n}`.\n\n For free-slip boundary conditions, consider using\n :meth:`add_nitsche_bc` instead of the penalty approach shown\n above. Nitsche provides variationally consistent enforcement\n without penalty tuning and is more robust on spherical shells.\n\n See Also\n --------\n add_nitsche_bc : Nitsche free-slip (recommended for curved boundaries).\n add_dirichlet_bc : For fixed-value boundary conditions.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": null, "is_public": true }, { - "name": "solve", + "name": "add_dirichlet_bc", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1660, - "signature": "(self, verbose = False)", - "parameters": [ - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1756, + "signature": "def add_dirichlet_bc(self, conds, boundary, components=None):", + "parameters": [], "returns": null, - "existing_docstring": "Solve by projecting each tensor component sequentially.", - "harvested_comments": [ - "Loop over the components of the tensor. If this is a symmetric", - "tensor, we'll usually be given the 1d form to prevent duplication", - "if self.t_field.sym_1d.shape != self.uw_function.shape:", - "raise ValueError(", - "\"Tensor shapes for uw_function and MeshVariable are not the same\"" - ], - "status": "minimal", + "existing_docstring": "\n Add a Dirichlet (essential) boundary condition.\n\n Dirichlet BCs fix the solution value at boundary nodes. This is\n the most common type of boundary condition for prescribing known\n values (e.g., fixed temperature, no-slip walls).\n\n Parameters\n ----------\n conds : array-like, float, or sympy.Matrix\n Boundary condition values. Use ``None`` or ``sympy.oo`` for\n unconstrained components (partial Dirichlet conditions).\n boundary : str\n Name of the boundary label (e.g., ``\"Top\"``, ``\"Bottom\"``).\n **Case-sensitive**: must match mesh boundary names exactly.\n components : array-like or None, optional\n Deprecated. Use ``None`` in ``conds`` for unconstrained components.\n\n Examples\n --------\n >>> # Scalar problem: fix temperature at boundaries\n >>> diffusion.add_dirichlet_bc(300.0, \"Top\")\n >>> diffusion.add_dirichlet_bc(500.0, \"Bottom\")\n\n >>> # Vector problem: no-slip walls (zero velocity)\n >>> stokes.add_dirichlet_bc([0.0, 0.0], \"Top\")\n >>> stokes.add_dirichlet_bc([0.0, 0.0], \"Bottom\")\n\n >>> # Free-slip: fix normal component, leave tangential free\n >>> stokes.add_dirichlet_bc([0.0, None], \"Left\") # x=0 at left\n >>> stokes.add_dirichlet_bc([None, 0.0], \"Bottom\") # y=0 at bottom\n\n >>> # Lid-driven cavity: moving top boundary\n >>> stokes.add_dirichlet_bc([1.0, 0.0], \"Top\")\n\n >>> # Symbolic boundary condition\n >>> x, y = mesh.X\n >>> T_boundary = 300 + 100 * sympy.sin(x * sympy.pi)\n >>> diffusion.add_dirichlet_bc(T_boundary, \"Top\")\n\n Raises\n ------\n KeyError\n If ``boundary`` name doesn't match any mesh boundary label.\n Check ``mesh.boundaries.keys()`` for available boundary names.\n\n See Also\n --------\n add_essential_bc : Alias for this method.\n add_natural_bc : For flux/traction boundary conditions.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Tensor_Projection", + "parent_class": null, "is_public": true }, { - "name": "F0", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1692, - "signature": "(self)", + "name": "u", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1819, + "signature": "def u(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise misfit term for scalar subproblem.", - "harvested_comments": [ - "backward compatibility" - ], - "status": "minimal", + "existing_docstring": "\n Primary unknown variable (MeshVariable) being solved for.\n\n For scalar problems (Poisson, advection-diffusion), this is typically a\n scalar field like temperature. For vector problems (Stokes), this is\n typically velocity.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Tensor_Projection", + "parent_class": null, "is_public": true }, { - "name": "F1", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1706, - "signature": "(self)", + "name": "DuDt", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1835, + "signature": "def DuDt(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise smoothing flux term for scalar subproblem.", - "harvested_comments": [ - "backward compatibility" - ], - "status": "minimal", + "existing_docstring": "\n Time derivative manager for advection-diffusion problems.\n\n This is a :class:`~underworld3.systems.ddt.SemiLagrangian_DDt` object\n that handles material derivatives using semi-Lagrangian advection.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Tensor_Projection", + "parent_class": null, "is_public": true }, { - "name": "uw_scalar_function", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1720, - "signature": "(self)", + "name": "DFDt", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1850, + "signature": "def DFDt(self):", "parameters": [], "returns": null, - "existing_docstring": "Current scalar component function being projected.", + "existing_docstring": "\n Flux time derivative manager for viscoelastic problems.\n\n This is a :class:`~underworld3.systems.ddt.SemiLagrangian_DDt` object\n that handles time evolution of stress/flux fields.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Tensor_Projection", + "parent_class": null, "is_public": true }, { - "name": "uw_scalar_function", + "name": "constitutive_model", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1725, - "signature": "(self, user_uw_function)", - "parameters": [ - { - "name": "user_uw_function", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1866, + "signature": "def constitutive_model(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the scalar component function for current tensor element.", + "existing_docstring": "\n Constitutive model defining the material behavior.\n\n The constitutive model provides the stress-strain relationship\n (for Stokes) or diffusivity (for advection-diffusion). Can be set\n as either a class or an instance.\n\n See Also\n --------\n underworld3.constitutive_models : Available constitutive models.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Tensor_Projection", + "parent_class": null, "is_public": true }, { - "name": "F0", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1906, - "signature": "(self)", + "name": "tau", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1925, + "signature": "def tau(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise source term including time derivative.", - "harvested_comments": [ - "backward compatibility" - ], - "status": "minimal", + "existing_docstring": "Computed flux from the constitutive model, projected onto the mesh.\n\n Returns a :class:`~underworld3.discretisation.MeshVariable` containing\n the flux that the solver actually computed. The variable is created\n lazily on first access and updated by solving a projection each time\n the property is read.\n\n For Stokes-family solvers, this is the deviatoric stress tensor\n :math:`\\boldsymbol{\\tau}`. For Poisson/diffusion, it is the\n diffusive flux :math:`\\kappa \\nabla u`. The projection handles\n derivative evaluation internally \u2014 users should use this property\n rather than manually reconstructing the flux from the constitutive\n formula.\n\n Subclasses may override this to return pre-computed values (e.g.\n VE_Stokes returns the stress stored during the solve).\n\n Returns\n -------\n MeshVariable\n Mesh variable containing the projected flux values.\n Access numerical data via ``.data`` or ``.array``, symbolic\n expression via ``.sym``.\n", + "harvested_comments": [], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "F1", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1920, - "signature": "(self)", + "name": "validate_solver", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2076, + "signature": "def validate_solver(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise diffusive flux term (Adams-Moulton integration).", - "harvested_comments": [ - "backward compatibility" - ], + "existing_docstring": "\n Checks to see if the required properties have been set.\nOver-ride this one if you want to check specifics for your solver", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "adv_diff_slcn_problem_description", + "name": "get_dof_partition", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1933, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2093, + "signature": "def get_dof_partition(self,\n section_type: str,\n filename: Optional[str | None] = None,\n outputPath: Optional[str] = \"\"):", "parameters": [], "returns": null, - "existing_docstring": "Build residual terms for advection-diffusion FEM assembly.", - "harvested_comments": [ - "f0 residual term", - "f1 residual term" - ], - "status": "minimal", + "existing_docstring": "\n Obtains how the degrees of freedom (DOF) are distributed/divided among the processors and saves them in an h5 file.\n Parameters\n ----------\n section_type:\n Can be: \"local\" which includes DOFs from ghost points or \"global\" which differentiates DOFs from ghost points by having negative values.\n filename:\n Output file name. If None, will print out results; if set to a string, the final output file will be _.u.h5.\n outputPath:\n Path of directory where data is saved. If left empty it will save the data in the current working directory.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1944, - "signature": "(self)", + "name": "boundary_flux", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2290, + "signature": "def boundary_flux(self, boundary, mass=\"lumped\", remove_mean=False, normal=None):", "parameters": [], "returns": null, - "existing_docstring": "Source term for the advection-diffusion equation.\n\nThe source :math:`f` appears on the right-hand side:\n\n.. math::\n \\frac{\\partial u}{\\partial t} + \\mathbf{V} \\cdot \\nabla u\n = \\nabla \\cdot (\\kappa \\nabla u) + f\n\nReturns\n-------\nsympy.Matrix\n Source term expression.", + "existing_docstring": "Consistent boundary flux on ``boundary``, recovered from the essential-BC\n reaction of the last solve (the Consistent Boundary Flux method).\n\n Returns ``(xs, flux)`` with one entry per boundary node on this rank: for a\n **scalar** solver the outward normal flux :math:`F\\cdot\\hat n` (e.g. surface heat\n flux :math:`-k\\,\\partial T/\\partial n`, whose boundary mean is the Nusselt\n number); for a **vector** solver the traction :math:`\\sigma\\cdot\\hat n` (pass\n ``normal`` to get the scalar normal component :math:`\\hat n\\cdot\\sigma\\cdot\\hat n`).\n\n ``mass`` de-smears the nodal reaction with the ``\"lumped\"`` (diagonal, monotone \u2014\n no overshoot at a flux jump) or ``\"consistent\"`` boundary mass. ``remove_mean``\n subtracts the boundary mean \u2014 leave ``False`` for a physical flux (the mean is\n the Nusselt number); ``True`` gives a gauge-free field (e.g. dynamic topography).\nParallel-safe and partition-independent.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "f", + "name": "boundary_flux_field", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1961, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2308, + "signature": "def boundary_flux_field(self, boundary, field, mass=\"lumped\",\n remove_mean=False, scale=1.0, normal=None):", + "parameters": [], "returns": null, - "existing_docstring": "Set the volumetric source term.", + "existing_docstring": "Write the consistent boundary flux (see :meth:`boundary_flux`) onto a scalar\n MeshVariable ``field`` at the boundary nodes (interior untouched), multiplied by\n ``scale``. This is the field hand-off for downstream machinery (surface heat\n flux for coupling, or \u2014 with ``remove_mean=True`` and ``scale=-1/(\\Delta\\rho g)``\n \u2014 dynamic topography). Returns ``field``.\n\n Note that ``scale`` is a generic multiplier, NOT the ``buoyancy_scale``\n taken by :meth:`dynamic_topography` / ``topography``: for topography the\n relationship is ``scale = -1 / buoyancy_scale`` (there the division by\n:math:`\\Delta\\rho\\,g` and the minus sign are internal).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "V_fn", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1967, - "signature": "(self)", + "name": "constant_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2485, + "signature": "def constant_nullspace(self):", "parameters": [], "returns": null, - "existing_docstring": "Velocity field for advection.\n\nThe advection velocity :math:`\\mathbf{V}` transports the scalar\nfield :math:`u`. Can be a MeshVariable or symbolic expression.\n\nReturns\n-------\nsympy.Matrix\n Velocity vector expression.", + "existing_docstring": "Attach a constant nullspace to the Jacobian before solve.\n\n For a scalar problem with only natural (Neumann) boundary\n conditions the operator is singular up to an additive\n constant. Set ``True`` to attach a constant\n ``PETSc.NullSpace`` to the Jacobian (and its transpose /\n the preconditioner), which both projects the constant mode\n out of the Krylov solve and makes PETSc remove the\n (consistent) component of the RHS \u2014 the scalar analogue of\n the Stokes pressure-nullspace handling. The RHS must be\n compatible (zero mean) for the Neumann problem to be\n solvable.\n", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "V_fn", + "name": "petsc_use_constant_nullspace", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1981, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2559, + "signature": "def petsc_use_constant_nullspace(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the velocity function for advection.\n\nParameters:\n-----------\nvalue : uw.discretisation.MeshVariable or sympy.Basic\n Velocity field as either a MeshVariable or sympy expression", - "harvested_comments": [ - "Mark as needing setup when velocity changes" - ], + "existing_docstring": "Backwards-compatible alias for :attr:`constant_nullspace`.\n\n The manifold-PDE work (PR #202) introduced this name on an\n older base; ``development`` independently landed the same\n capability as :attr:`constant_nullspace` (with an internal\n pure-Neumann guard and a cached nullspace object). Both names\n now refer to that single canonical implementation.\n", + "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "delta_t", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 1997, - "signature": "(self)", + "name": "tolerance", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2575, + "signature": "def tolerance(self):", "parameters": [], "returns": null, - "existing_docstring": "Timestep for time integration.\n\nThe timestep :math:`\\Delta t` controls the temporal discretization.\nFor explicit advection, this should satisfy the CFL condition:\n\n.. math::\n \\Delta t < \\frac{h}{|\\mathbf{V}|}\n\nwhere :math:`h` is the element size and :math:`|\\mathbf{V}|` is the\nvelocity magnitude.\n\nReturns\n-------\nUWexpression\n Timestep value.\n\nSee Also\n--------\nestimate_dt : Computes a stable timestep automatically.", + "existing_docstring": "\n Solver convergence tolerance for SNES and KSP.\n\n Setting this value automatically configures related PETSc tolerances:\n - ``snes_rtol``: Set to ``tolerance``\n - ``ksp_rtol``: Set to ``tolerance * 0.1``\n - ``ksp_atol``: Set to ``tolerance * 1e-6``\n\n Returns\n -------\n float\n Current solver tolerance.\n\n Examples\n --------\n >>> solver.tolerance = 1e-6 # Tighter convergence\n >>> solver.solve()\n", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "delta_t", + "name": "solve", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2021, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3053, + "signature": "def solve(self,\n zero_init_guess: bool =True,\n _force_setup: bool =False,\n verbose: bool=False,\n debug: bool=False,\n debug_name: str=None,\n time=None,\n divergence_retries: int=0, ):", + "parameters": [], "returns": null, - "existing_docstring": "Set the timestep (handles unit conversion if provided).", - "harvested_comments": [ - "Handle Pint Quantities with time dimensions", - "This is a Pint Quantity - check if it has time dimensions", - "Convert physical time to nondimensional using model time scale", - "Physical time / time scale = nondimensional time", - "Must use to_reduced_units() to convert both quantities" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SNES_AdvectionDiffusion", + "existing_docstring": "\n Solve the system of equations.\n\n Assembles and solves the discretized PDE system using PETSc's SNES\n (Scalable Nonlinear Equations Solvers) framework. The solution is\n stored in the solver's unknown variable(s).\n\n Parameters\n ----------\n zero_init_guess : bool, default=True\n If True, use zero as the initial guess. If False, use the current\n values in the solution variable(s) as the initial guess, which can\n improve convergence for time-stepping or continuation methods.\n _force_setup : bool, default=False\n Force rebuild of the solver even if already set up. Useful after\n changing boundary conditions or constitutive parameters.\n verbose : bool, default=False\n Print solver progress and timing information.\n debug : bool, default=False\n Enable debug output including intermediate residuals.\n debug_name : str, optional\n Name prefix for debug output files.\n time : float or Quantity, optional\n Physical time for this solve. Passed as ``petsc_t`` to all\n pointwise functions. Expressions using ``mesh.t`` evaluate at\n this time. Non-dimensionalised when scaling is active.\n Default: None (petsc_t unchanged).\n divergence_retries : int, default=0\n If SNES reports DIVERGED after the solve, re-call it with warm\n start up to this many times. A single retry rescues most VEP\n yield-surface kink divergences. 0 preserves legacy behaviour.\n\n Returns\n -------\n None\n Solution is stored in ``self.u`` (and ``self.p`` for Stokes).\n\n Examples\n --------\n >>> # Basic solve\n >>> solver.solve()\n >>> temperature_values = solver.u.array[:, 0, 0]\n\n >>> # Time-stepping with previous solution as initial guess\n >>> for step in range(n_steps):\n ... solver.solve(zero_init_guess=False)\n\n >>> # Check convergence\n >>> print(f\"Converged: {solver.snes.getConvergedReason() > 0}\")\n\n Notes\n -----\n This is a **collective operation** - all MPI ranks must call it.\n The solver automatically handles mesh variable synchronization.\n\n See Also\n --------\n snes : Access to underlying PETSc SNES object for advanced control.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "estimate_dt", + "name": "tolerance", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2066, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3423, + "signature": "def tolerance(self):", "parameters": [], "returns": null, - "existing_docstring": "Estimate an appropriate timestep for the advection-diffusion solver.\n\nThis is an implicit solver so the returned :math:`\\delta t` is the\nminimum of:\n\n- :math:`\\delta t_{\\textrm{diff}}`: typical time for diffusion across an element\n- :math:`\\delta t_{\\textrm{adv}}`: typical element-crossing time for a fluid parcel\n\nReturns\n-------\npint.Quantity or float\n The recommended timestep with physical time units if a model\n with reference scales is available, otherwise nondimensional.", - "harvested_comments": [ - "## required modules", - "Use the unified .K property from the constitutive model", - "This provides diffusivity for diffusion models", - "Evaluate the diffusivity (handles constant and spatially-varying cases)", - "If diffusivity is unit-aware (UnitAwareArray), nondimensionalise it to get" - ], + "existing_docstring": "\n Solver convergence tolerance for SNES and KSP.\n\n Setting this value automatically configures related PETSc tolerances:\n - ``snes_rtol``: Set to ``tolerance``\n - ``ksp_rtol``: Set to ``tolerance * 0.1``\n - ``ksp_atol``: Set to ``tolerance * 1e-6``\n\n Returns\n -------\n float\n Current solver tolerance.\n", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "solve", + "name": "add_nitsche_bc", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2198, - "signature": "(self, zero_init_guess: bool = True, timestep: float = None, _force_setup: bool = False, _evalf = False, verbose = False)", - "parameters": [ - { - "name": "zero_init_guess", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "timestep", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "_force_setup", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "_evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3447, + "signature": "def add_nitsche_bc(self, conds=None, boundary=None, direction=None,\n normal=None, gamma=10.0, theta=1, mask=None,\n local_h=True, g=None):", + "parameters": [], "returns": null, - "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.", - "harvested_comments": [ - "this will force an initialisation because the functions need to be updated", - "Update History / Flux History terms", - "SemiLagrange and Lagrange may have different sequencing.", - "Invalidate cached data views - PETSc may have replaced underlying buffers", - "This ensures .data and .array properties return fresh data from PETSc" - ], + "existing_docstring": "Add Nitsche weak enforcement of a velocity constraint along a direction.\n\n For vector solvers (no pressure field), this constrains\n :math:`\\mathbf{u} \\cdot \\mathbf{d} = \\mathrm{conds}` on the boundary\n using Nitsche's method with penalty, consistency, and symmetry terms.\n\n Parameters\n ----------\n conds : sympy expression or float, optional\n Prescribed velocity along the constraint direction. Default zero\n (free-slip when the direction is the surface normal).\n boundary : str\n Boundary label.\n direction : sympy.Matrix or list, optional\n Constraint direction. Default ``None`` uses surface normal.\n normal : sympy.Matrix or list, optional\n Boundary unit normal used in the Nitsche consistency and symmetry\n terms \u2014 the same geometric-normal override as on the Stokes\n variant. Default ``None`` uses the per-boundary,\n deformation-tracking ``mesh.boundary_normal(boundary)``.\n gamma : float, default=10.0\n Dimensionless stabilisation parameter.\n theta : {-1, 0, 1}, default=1\n Symmetry parameter (1=symmetric, -1=skew-symmetric).\n mask : sympy expression, optional\n Accepted for signature parity with the Stokes variant, but\n one-sided masking is **not implemented** on vector solvers:\n passing a mask raises ``NotImplementedError``.\n local_h : bool, default=True\n Scale the penalty by a local per-cell mesh size\n (:meth:`Mesh.cell_size`) rather than the global minimum\n (:meth:`Mesh.get_min_radius`). See\n ``SNES_Stokes_SaddlePt.add_nitsche_bc`` for details.\n g : sympy expression or float, optional\n Deprecated keyword alias for ``conds`` (one DeprecationWarning).\n\n Warnings\n --------\n Exterior boundaries only. See ``SNES_Stokes_SaddlePt.add_nitsche_bc``\n for details on why internal boundaries are not supported.\n\n Notes\n -----\n The legacy boundary-first call ``add_nitsche_bc(boundary, g=...)`` is\n detected conservatively (first positional argument a string while the\n second is not \u2014 a BC datum is never a string) and shimmed with one\n DeprecationWarning; see :meth:`_value_first_bc_args`.\n\n See Also\n --------\n SNES_Stokes_SaddlePt.add_nitsche_bc : Stokes version with pressure coupling.\n", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": true }, { - "name": "F0", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2412, - "signature": "(self)", + "name": "solve", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4140, + "signature": "def solve(self,\n zero_init_guess: bool =True,\n _force_setup: bool =False,\n verbose=False,\n debug=False,\n debug_name=None,\n divergence_retries: int=0,\n ):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise source term including time derivative.", - "harvested_comments": [ - "backward compatibility" - ], + "existing_docstring": "\n Solve the vector field system of equations.\n\n Assembles and solves the discretized PDE system for vector unknowns\n (e.g., velocity in projection problems) using PETSc's SNES framework.\n\n Parameters\n ----------\n zero_init_guess : bool, default=True\n If True, use zero as the initial guess. If False, use the current\n values in ``self.u`` as the initial guess.\n _force_setup : bool, default=False\n Force rebuild of the solver even if already set up.\n verbose : bool, default=False\n Print solver progress and timing information.\n debug : bool, default=False\n Enable debug output.\n debug_name : str, optional\n Name prefix for debug output files.\n divergence_retries : int, default=0\n If SNES reports DIVERGED after the solve, re-call it with warm\n start up to this many times. 0 preserves legacy behaviour.\n\n Returns\n -------\n None\n Solution is stored in ``self.u``.\n\n Notes\n -----\n This is a **collective operation** - all MPI ranks must call it.\n\n See Also\n --------\n u : The solution vector field variable.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "n_components", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4448, + "signature": "def n_components(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Number of scalar DOF components per node.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "F1", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2426, - "signature": "(self)", + "name": "solve", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4913, + "signature": "def solve(self,\n zero_init_guess: bool = True,\n _force_setup: bool = False,\n verbose=False,\n debug=False,\n debug_name=None,\n divergence_retries: int=0,\n ):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise diffusive flux term.", - "harvested_comments": [ - "backward compatibility" - ], - "status": "minimal", + "existing_docstring": "Solve the multi-component SNES problem.\n\n Collective across all MPI ranks. The solution is written back to\n ``self.u.vec`` and made available through ``self.u.array``.\n\n Parameters\n ----------\n divergence_retries : int, default=0\n If SNES reports DIVERGED after the solve, re-call it with warm\n start up to this many times. 0 preserves legacy behaviour.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2440, - "signature": "(self)", + "name": "add_rotated_freeslip_bc", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5338, + "signature": "def add_rotated_freeslip_bc(self, conds=None, boundary=None, normal=None):", "parameters": [], "returns": null, - "existing_docstring": "Source term for the diffusion equation.", + "existing_docstring": "Add STRONG free-slip (:math:`\\mathbf{u}\\cdot\\hat{\\mathbf n}=0`) by rotating\n the boundary velocity DOFs into a per-node (normal, tangential) frame and\n imposing the rotated normal component as an exact Dirichlet constraint.\n\n Unlike Nitsche/penalty free-slip (weak, leaks :math:`\\mathcal O(10^{-3})`),\n this enforces zero wall-normal flow to machine precision, and the constraint\n **reaction** is the consistent boundary normal traction\n :math:`\\sigma_{nn}` (see :meth:`boundary_normal_traction`) with no\n augmented-Lagrangian splitting. Correct on deformed / tilted / curved\n boundaries because the normal is taken per node.\n\n Parameters\n ----------\n conds : float or None, optional\n Prescribed wall-normal velocity datum, in the canonical value-first\n BC order (Style Charter, API conventions). Only the homogeneous\n free-slip constraint :math:`\\mathbf{u}\\cdot\\hat{\\mathbf n}=0` is\n implemented, so this must be zero (or ``None``, meaning zero); a\n non-zero datum raises ``NotImplementedError`` (use\n :meth:`add_nitsche_bc` or ``add_constraint_bc`` for prescribed\n normal in/outflow).\n boundary : str\n Boundary label to constrain.\n normal : None or sympy 1\u00d7dim Matrix or array, optional\n Per-node outward normal source. ``None`` uses the geometric facet\n normal (PETSc ``computeCellGeometryFVM``; works in 2D and 3D). A\n sympy ``1\u00d7dim`` matrix supplies an analytic normal (exact\n ``X/|X|`` on a spherical cap, a constant on a planar face) \u2014 preferred\n on curved boundaries. A constant array is also accepted.\n\n Notes\n -----\n A node shared by several rotated-free-slip boundaries (a box corner, a 3D\n edge) is constrained on the whole span of its accumulated normals: a 3D\n face frees two tangential directions, a 3D edge frees one (the edge\n tangent), a corner is fully pinned. Registering delegates the solve to\n :mod:`underworld3.utilities.rotated_bc`.\n\n The legacy boundary-first call ``add_rotated_freeslip_bc(boundary,\n normal)`` is detected conservatively (the first positional argument is\n a string \u2014 the datum is never a string) and shimmed with one\n DeprecationWarning: the string becomes ``boundary`` and a second\n positional argument, if present, becomes ``normal``.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "f", + "name": "boundary_normal_traction", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2445, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5460, + "signature": "def boundary_normal_traction(self, boundary, mass=\"lumped\"):", + "parameters": [], "returns": null, - "existing_docstring": "Set the volumetric source term.", + "existing_docstring": "Return the boundary normal traction :math:`\\sigma_{nn}` on a\n rotated-free-slip ``boundary`` as the constraint reaction from the last\n solve \u2014 the smooth, bounded quantity used for dynamic topography\n (:math:`h_\\infty=-(\\sigma_{nn}-\\overline{\\sigma_{nn}})/\\rho g`). Requires a\n prior :meth:`add_rotated_freeslip_bc` on ``boundary`` and a completed\n :meth:`solve`.\n\n ``mass`` chooses the boundary-mass de-smear of the nodal reaction:\n ``\"lumped\"`` (default) is monotone \u2014 it cannot overshoot where the traction\n jumps (e.g. across a viscosity contrast), so it is the safe choice for driving\n a free surface; ``\"consistent\"`` uses the full P2 line mass (marginally sharper\non smooth tractions, but overshoots at discontinuities).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "delta_t", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2451, - "signature": "(self)", + "name": "dynamic_topography", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5479, + "signature": "def dynamic_topography(self, boundary, field, buoyancy_scale=1.0, mass=\"lumped\"):", "parameters": [], "returns": null, - "existing_docstring": "Timestep for time integration.", + "existing_docstring": "Write the dynamic topography\n :math:`h = -(\\sigma_{nn}-\\overline{\\sigma_{nn}})/(\\Delta\\rho\\,g)` on a\n rotated-free-slip ``boundary`` onto a scalar MeshVariable ``field``, from the\n constraint reaction of the last solve. This is the hand-off to the free-surface\n machinery \u2014 the 3-number topography integrator drives node motion from a surface\n field, so create a scalar ``field`` (P1 recommended, continuous) up front and\n pass it here after each :meth:`solve`; its boundary nodes are filled and the\n interior left untouched.\n\n ``buoyancy_scale`` is :math:`\\Delta\\rho\\,g` (traction \u2192 length). ``mass`` selects\n the recovery de-smear (``\"lumped\"`` default is monotone \u2014 no overshoot at a\n stress jump \u2014 and is the safe choice for a free surface). Requires a prior\n:meth:`add_rotated_freeslip_bc` on ``boundary`` and a completed :meth:`solve`.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "delta_t", + "name": "add_nitsche_bc", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2456, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5500, + "signature": "def add_nitsche_bc(self, conds=None, boundary=None, direction=None, normal=None,\n gamma=10.0, theta=1, mask=None, local_h=True, g=None):", + "parameters": [], "returns": null, - "existing_docstring": "Set the timestep (handles unit conversion if provided).", - "harvested_comments": [ - "Handle Pint Quantities with time dimensions", - "This is a Pint Quantity - check if it has time dimensions", - "Convert physical time to nondimensional using model time scale", - "Physical time / time scale = nondimensional time", - "Must use to_reduced_units() to convert both quantities" - ], - "status": "minimal", + "existing_docstring": "Add Nitsche weak enforcement of a velocity constraint along a direction.\n\n Nitsche's method provides a variationally consistent alternative to\n penalty-based free-slip that is less sensitive to the penalty magnitude\n and gives optimal convergence rates.\n\n By default, constrains the normal velocity component\n :math:`\\mathbf{u} \\cdot \\mathbf{n} = \\mathrm{conds}` (free-slip when\n ``conds`` is zero). When *direction* is provided, constrains\n :math:`\\mathbf{u} \\cdot \\mathbf{d} = \\mathrm{conds}` along that\n direction instead.\n\n The method constructs boundary residuals and Jacobians for:\n\n - Penalty/stabilisation: :math:`(\\gamma \\mu / h)(\\mathbf{u} \\cdot \\mathbf{d} - g) \\, \\mathbf{d}`\n - Consistency: :math:`-(\\boldsymbol{\\sigma} \\cdot \\mathbf{n} \\cdot \\mathbf{d}) \\, \\mathbf{d}`\n (boundary traction projected onto constraint direction)\n - Symmetry: :math:`-\\theta \\mu` adjoint consistency term\n - Pressure: :math:`p \\, (\\mathbf{n} \\cdot \\mathbf{d}) \\, \\mathbf{d}` on velocity\n and :math:`(\\mathbf{n} \\cdot \\mathbf{d})(\\mathbf{u} \\cdot \\mathbf{d} - g)` on pressure\n\n Parameters\n ----------\n conds : sympy expression or float, optional\n Prescribed velocity along the constraint direction. Default\n ``None`` means zero (:math:`\\mathbf{u} \\cdot \\mathbf{d} = 0`).\n boundary : str\n Boundary label (e.g., ``\"Upper\"``, ``\"Lower\"``).\n direction : sympy.Matrix or list, optional\n Constraint direction vector. Default ``None`` uses the boundary\n surface normal (free-slip). Can be spatially varying (e.g.,\n a fault orientation field).\n normal : sympy.Matrix or list, optional\n Boundary unit normal used in the Nitsche consistency, symmetry,\n and pressure-coupling terms. Default ``None`` uses the per-boundary,\n deformation-tracking ``mesh.boundary_normal(boundary)``.\n gamma : float, default=10.0\n Dimensionless stabilisation parameter. Typical values 5--20\n for P2 elements.\n theta : {-1, 0, 1}, default=1\n Symmetry parameter:\n 1: symmetric (default \u2014 optimal convergence and solver efficiency)\n 0: incomplete (no symmetry term)\n -1: skew-symmetric (unconditionally stable but slower convergence)\n mask : sympy expression, optional\n Element-wise mask for one-sided application on internal\n boundaries. Use a DG MeshVariable that is 1 on the active\n side and 0 on the inactive side. The mask multiplies all\n Nitsche terms so that only the active-side cell contributes.\n local_h : bool, default=True\n Scale the penalty term :math:`\\gamma\\mu/h` by a **local**,\n per-cell mesh size (:meth:`Mesh.cell_size`, deformation- and\n adaptation-tracking) rather than the single **global** minimum\n cell size (:meth:`Mesh.get_min_radius`). On a non-uniform or\n adaptive mesh the local size scales the stabilisation correctly\n on every facet; on a uniform mesh the two coincide. Set ``False``\n to restore the legacy global-h behaviour exactly.\n g : sympy expression or float, optional\n Deprecated keyword alias for ``conds`` (one DeprecationWarning).\n\n Examples\n --------\n >>> # Free-slip (u.n = 0)\n >>> stokes.add_nitsche_bc(0.0, \"Upper\", gamma=10)\n\n >>> # Prescribed normal inflow\n >>> stokes.add_nitsche_bc(1.0, \"Left\", gamma=10)\n\n >>> # Constrain along a specific direction (e.g. fault normal)\n >>> fault_normal = sympy.Matrix([0.6, 0.8])\n >>> stokes.add_nitsche_bc(0.0, \"Fault\", direction=fault_normal, gamma=10)\n\n Notes\n -----\n The legacy boundary-first call ``add_nitsche_bc(boundary, g=...)`` is\n detected conservatively (first positional argument a string while the\n second is not \u2014 a BC datum is never a string) and shimmed with one\n DeprecationWarning; see :meth:`_value_first_bc_args`.\n\n Warnings\n --------\n This method is for **exterior** boundaries only. On internal\n boundaries (e.g., ``\"Internal\"`` from ``AnnulusInternalBoundary``),\n the consistency terms cancel between the two adjacent cells,\n producing worse results than no constraint. Use the penalty\n approach (``add_natural_bc``) for internal boundary constraints.\n\n References\n ----------\n Sime & Wilson (2020), arXiv:2001.10639 \u2014 Nitsche free-slip for geodynamics.\n PETSc ``snes/tutorials/ex62.c`` \u2014 Nitsche Stokes implementation.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "estimate_dt", + "name": "tolerance", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2501, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5766, + "signature": "def tolerance(self):", "parameters": [], "returns": null, - "existing_docstring": "Estimate an appropriate timestep for the diffusion solver.\n\nThis solver only has a diffusive component, so the returned\n:math:`\\delta t` is:\n\n- :math:`\\delta t_{\\textrm{diff}}`: typical time for diffusion across an element\n\nReturns\n-------\npint.Quantity or float\n The diffusive timestep with physical time units if a model\n with reference scales is available, otherwise nondimensional.", - "harvested_comments": [ - "## required modules", - "Use the unified .K property from the constitutive model", - "This provides diffusivity for diffusion models", - "Evaluate the diffusivity (handles constant and spatially-varying cases)", - "If diffusivity is unit-aware (UnitAwareArray), nondimensionalise it to get" - ], + "existing_docstring": "\n Solver convergence tolerance for the Stokes saddle-point system.\n\n Setting this value automatically configures PETSc tolerances for the\n coupled velocity-pressure solve using Schur complement fieldsplit:\n - ``snes_rtol``: Set to ``tolerance``\n - ``ksp_atol``: Set to ``tolerance * 1e-6``\n - ``fieldsplit_pressure_ksp_rtol``: Set to ``tolerance * 0.1``\n - ``fieldsplit_velocity_ksp_rtol``: Set to ``tolerance * 0.033``\n\n Also enables Eisenstat-Walker adaptive tolerance (``snes_ksp_ew``).\n\n Returns\n -------\n float\n Current solver tolerance.\n\n Examples\n --------\n >>> stokes.tolerance = 1e-6 # Tighter convergence\n >>> stokes.solve()\n", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "solve", + "name": "strategy", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2576, - "signature": "(self, zero_init_guess: bool = True, timestep: float = None, evalf: bool = False, _force_setup: bool = False, verbose = False)", - "parameters": [ - { - "name": "zero_init_guess", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "timestep", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "evalf", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "_force_setup", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5804, + "signature": "def strategy(self):", + "parameters": [], "returns": null, - "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.", - "harvested_comments": [ - "this will force an initialisation because the functions need to be updated", - "self._flux = self.constitutive_model.flux.T", - "self._flux_star = self._flux.copy()", - "Update History / Flux History terms", - "SemiLagrange and Lagrange may have different sequencing." - ], + "existing_docstring": "\n Solver strategy controlling preconditioner configuration.\n\n Currently supports:\n - ``\"default\"``: Standard Schur complement fieldsplit with GAMG\n - ``\"robust\"``: (Reserved) More robust but slower configuration\n - ``\"fast\"``: (Reserved) Faster but less robust configuration\n\n Setting this property reconfigures the entire preconditioner stack.\n\n Returns\n -------\n str\n Current strategy name.\n", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Diffusion", + "parent_class": null, "is_public": true }, { - "name": "F0", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2813, - "signature": "(self)", + "name": "PF0", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5890, + "signature": "def PF0(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise momentum source term (body force + inertia).", - "harvested_comments": [ - "I think this should be bdf(1) ... the higher order", - "terms are introduced through the adams_moulton fluxes" - ], - "status": "minimal", + "existing_docstring": "\n Pressure constraint term (incompressibility and other constraints).\n\n This is the :math:`\\\\mathbf{h}_0(p)` term in the saddle-point formulation,\n typically representing the incompressibility constraint\n :math:`\\\\nabla \\\\cdot \\\\mathbf{u} = 0`.\n\n Returns\n -------\n UWexpression\n Symbolic expression for the constraint term.\n\n See Also\n --------\n F0 : Velocity force term.\n F1 : Velocity flux/stress term.\n", + "harvested_comments": [], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "F1", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2831, - "signature": "(self)", + "name": "p", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5917, + "signature": "def p(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise stress flux term (viscous + pressure).", - "harvested_comments": [ - "We can flag to only do this if the constitutive model has been updated", - "Is the else condition useful - other than to prevent a crash ?", - "Yes, because then it can just live on the Stokes solver ..." - ], - "status": "minimal", + "existing_docstring": "\n Pressure solution variable (MeshVariable).\n\n The pressure field from the Stokes solve, typically a discontinuous\n field one degree lower than velocity.\n\n Returns\n -------\n MeshVariable\n Pressure field variable.\n\n See Also\n --------\n u : Velocity solution variable.\n", + "harvested_comments": [], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "PF0", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2862, - "signature": "(self)", + "name": "saddle_preconditioner", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5941, + "signature": "def saddle_preconditioner(self):", "parameters": [], "returns": null, - "existing_docstring": "Pointwise constraint term (continuity equation).", + "existing_docstring": "\n Custom preconditioner for the pressure Schur complement.\n\n A symbolic expression used to precondition the pressure solve.\n If None (default), uses the mass matrix approximation.\n\n Returns\n -------\n sympy expression or None\n Custom preconditioner expression.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "navier_stokes_problem_description", + "name": "petsc_use_nullspace", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2877, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5961, + "signature": "def petsc_use_nullspace(self):", "parameters": [], "returns": null, - "existing_docstring": "Build residual terms for Navier-Stokes FEM assembly (deprecated).", - "harvested_comments": [ - "f0 residual term", - "f1 residual term", - "p1 residual term" - ], - "status": "minimal", + "existing_docstring": "Enable full nullspace handling: constant pressure + mesh rotation modes.\n\n Convenience property that enables the pressure nullspace and\n auto-populates velocity nullspace modes from ``mesh.nullspace_rotations``.\n\n For finer control, use ``petsc_use_pressure_nullspace`` and\n ``petsc_velocity_nullspace_basis`` separately.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "delta_t", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2891, - "signature": "(self)", + "name": "petsc_use_pressure_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5990, + "signature": "def petsc_use_pressure_nullspace(self):", "parameters": [], "returns": null, - "existing_docstring": "Timestep for time integration.", + "existing_docstring": "\n Enable PETSc handling of the constant-pressure nullspace.\n\n When enabled, the solver attaches the constant-pressure mode to\n the coupled Stokes nullspace basis before solve. Additional\n user-supplied velocity nullspace modes can be configured through\n ``petsc_velocity_nullspace_basis``.\n\n For free-slip shell problems this is typically used together with\n the rigid-body rotation modes documented on\n ``petsc_velocity_nullspace_basis``.\n\n Examples\n --------\n 2-D annulus with pressure gauge only:\n >>> stokes.petsc_use_pressure_nullspace = True\n\n 2-D annulus with pressure plus rigid rotation:\n >>> x, y = mesh.X\n >>> stokes.petsc_use_pressure_nullspace = True\n >>> stokes.petsc_velocity_nullspace_basis = [sympy.Matrix([-y, x])]\n\n 3-D spherical shell with pressure plus the three rigid rotations:\n >>> x, y, z = mesh.X\n >>> stokes.petsc_use_pressure_nullspace = True\n >>> stokes.petsc_velocity_nullspace_basis = [\n ... sympy.Matrix([0, -z, y]),\n ... sympy.Matrix([z, 0, -x]),\n ... sympy.Matrix([-y, x, 0]),\n ... ]\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "delta_t", + "name": "multiplier_schur_pc", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2896, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6035, + "signature": "def multiplier_schur_pc(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the timestep value.", + "existing_docstring": "\n Give each Lagrange-multiplier (constraint) block its own viscosity-scaled\n Schur preconditioner.\n\n The constraint Schur complement ``S_lambda = C A^-1 C^T`` scales as\n ``1/mu`` (since ``A ~ mu K``), exactly like the pressure Schur\n ``S_p ~ mu^-1 M_p`` and **independent of the augmentation** ``r``. When\n enabled, the multiplier block's *preconditioner* (Pmat) diagonal reuses\n pressure's ``1/mu`` mass while the true operator (Amat) block stays the\n screening ``eps`` \u2014 so Newton is unchanged and this is a pure\n preconditioner term (the boundary-trace analog of the pressure\n ``saddle_preconditioner``).\n\n Effect: convergence decouples from ``r``. On moderate boundary viscosity\n contrast (e.g. annulus mu_hi ~ 1e3) the augmentation becomes optional\n (``augmentation=0`` converges) and the recovered multiplier (= dynamic\n topography) is cleaner at small ``r``. On extreme contrast (eta jump 1e6)\n a small augmentation floor is still needed, but the requirement shrinks by\n orders of magnitude. Pair with ``snes_type='ksponly'`` for linear Stokes \u2014\n the block solver's default ``newtonls`` defect-corrects a linear system in\n many steps when the Schur approximation is stiff.\n\n Default ``False`` (opt-in). On the fieldsplit/iterative path it is\n bit-identical on uniform ``mu`` and cracks the moderate-contrast wall;\n but a monolithic ``lu`` solve factorizes the Pmat (``pc_use_amat`` is a\n no-op there), so this term is not inert for direct solves \u2014 hence opt-in.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "rho", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2902, - "signature": "(self)", + "name": "petsc_velocity_nullspace_basis", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6071, + "signature": "def petsc_velocity_nullspace_basis(self):", "parameters": [], "returns": null, - "existing_docstring": "Fluid density.", + "existing_docstring": "\n Optional exact velocity nullspace modes for the coupled Stokes solve.\n\n Each entry must be a vector-valued SymPy expression defined in the\n mesh coordinate system and representing an exact null mode of the\n configured Stokes operator. Typical examples are rigid-body rotation\n modes for annulus or spherical-shell free-slip problems.\n\n For centered shell geometries with exact free-slip / no-penetration\n boundary conditions, the rigid-body rotation modes are:\n\n - 2-D annulus: one mode, ``(-y, x)``, equivalent to ``r e_theta``\n - 3-D spherical shell: three modes,\n ``(0, -z, y)``, ``(z, 0, -x)``, and ``(-y, x, 0)``\n\n These are the velocity fields generated by rigid rotations\n ``u = omega x x``. They are tangent to concentric circles / spheres\n and have zero strain rate, so they are exact velocity null modes for\n the free-slip shell Stokes operator.\n\n They are not exact null modes when the boundary conditions select a\n specific tangential velocity, for example:\n\n - essential velocity boundary conditions\n - penalty boundary conditions on the full velocity error\n ``u - u_analytic``\n\n To remove shell nullspaces in Stokes, set the pressure mode and then\n provide the exact rotation basis:\n\n Examples\n --------\n 2-D annulus:\n >>> x, y = mesh.X\n >>> stokes.petsc_use_pressure_nullspace = True\n >>> stokes.petsc_velocity_nullspace_basis = [sympy.Matrix([-y, x])]\n\n 3-D spherical shell:\n >>> x, y, z = mesh.X\n >>> stokes.petsc_use_pressure_nullspace = True\n >>> stokes.petsc_velocity_nullspace_basis = [\n ... sympy.Matrix([0, -z, y]),\n ... sympy.Matrix([z, 0, -x]),\n ... sympy.Matrix([-y, x, 0]),\n ... ]\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "rho", + "name": "validate_solver", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2907, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6498, + "signature": "def validate_solver(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set the fluid density.", + "existing_docstring": "Checks to see if the required properties have been set", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "f", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2913, - "signature": "(self)", + "name": "get_dof_partition", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6520, + "signature": "def get_dof_partition(self,\n section_type: str,\n filename: Optional[str | None] = None,\n outputPath: Optional[str] = \"\"):", "parameters": [], "returns": null, - "existing_docstring": "Source term for the momentum equation.", + "existing_docstring": "\n Obtains how the degrees of freedom (DOF) are distributed/divided among the processors and saves them in an h5 file.\n Parameters\n ----------\n section_type:\n Can be: \"local\" which includes DOFs from ghost points or \"global\" which differentiates DOFs from ghost points by having negative values.\n filename:\n Output file name. If None, will print out results; if set to a string, the output files will be _.u.h5 and _.p.h5.\n outputPath:\n Path of directory where data is saved. If left empty it will save the data in the current working directory.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "f", + "name": "set_active_region", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2918, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7605, + "signature": "def set_active_region(self, region_label_name, region_label_value):", + "parameters": [], "returns": null, - "existing_docstring": "Set the volumetric source term.", + "existing_docstring": "Configure the solver to assemble only on cells in the given region.\n\n Cells NOT in the specified region get a trivial DS (no volume\n contributions) and their DOFs should be pinned via Dirichlet BCs.\n\n Parameters\n ----------\n region_label_name : str\n DM label name for the INACTIVE region (e.g., \"Outer\").\n region_label_value : int\n Label stratum value for the inactive region (e.g., 102).\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "div_u", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2924, - "signature": "(self)", + "name": "compute_volume_residual_fields", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7665, + "signature": "def compute_volume_residual_fields(\n self,\n time=None,\n verbose=False,\n cell_indices=None,\n residual_field_id=None,\n include_boundary_terms=False,\n ):", "parameters": [], "returns": null, - "existing_docstring": "Velocity divergence: :math:`\\nabla \\cdot \\mathbf{u} = \\mathrm{tr}(\\dot{\\varepsilon})`.", + "existing_docstring": "Return PETSc FEM residual fields in each solver field's local layout.\n\n This is a low-level diagnostic hook for post-processing derived\n boundary quantities such as consistent-boundary-flux traction. By\n default it calls PETSc's ``DMPlexSNESComputeResidualFEM`` directly. If\n ``cell_indices`` is supplied, it instead calls\n a UW wrapper around ``DMPlexComputeResidualByKey`` on a cloned DM with\n a copied ``PetscDS`` that has no registered boundary objects, so the\n selected-cell path returns volume terms only. Set\n ``include_boundary_terms=True`` to call PETSc's original keyed\n residual behavior, which appends registered boundary residuals. The\n returned arrays are local to each rank and have the same flat layout as\n the corresponding MeshVariable PETSc vector.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "strainrate", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2931, - "signature": "(self)", + "name": "compute_boundary_residual_fields", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7804, + "signature": "def compute_boundary_residual_fields(self, boundary, time=None, verbose=False, residual_field_id=0):", "parameters": [], "returns": null, - "existing_docstring": "Symmetric strain rate tensor (with 1/2 factor).\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)", + "existing_docstring": "Return the registered FEM boundary residual for one named boundary.\n\n This is a low-level diagnostic hook for weak-boundary-condition\n debugging. It assembles PETSc's boundary residual terms registered on\n ``boundary`` through ``DMPlexComputeBdResidualSingle``. For Nitsche\n free slip, this includes the full registered weak boundary residual,\n not only the scalar penalty term. The returned arrays are local to each\n rank and have the same flat layout as the corresponding MeshVariable\n PETSc vector.\n", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "DuDt", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2941, - "signature": "(self)", + "name": "get_local_field_is", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7943, + "signature": "def get_local_field_is(section, field, unconstrained=False):", "parameters": [], "returns": null, - "existing_docstring": "Time derivative operator for velocity.", + "existing_docstring": "\n This function returns the index set of unconstrained points if True, or all points if False.\n", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "DuDt", + "name": "solve", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2946, - "signature": "(self, DuDt_value: Union[SemiLagrangian_DDt, Lagrangian_DDt])", - "parameters": [ - { - "name": "DuDt_value", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 8054, + "signature": "def solve(self,\n zero_init_guess: bool = True,\n picard: int = 0,\n verbose=False,\n debug=False,\n debug_name=None,\n _force_setup: bool =False,\n time=None,\n divergence_retries: int = 0, ):", + "parameters": [], "returns": null, - "existing_docstring": "Set the time derivative operator for velocity.", + "existing_docstring": "\n Solve the Stokes system for velocity and pressure.\n\n Assembles and solves the coupled velocity-pressure system using a\n saddle-point formulation. Handles nonlinear rheologies through\n Newton or Picard iteration.\n\n Parameters\n ----------\n zero_init_guess : bool, default=True\n If True, use zero as the initial guess. If False, use current\n values in ``self.u`` (velocity) and ``self.p`` (pressure) as\n initial guess. Using False can improve convergence for\n time-stepping or parameter continuation.\n picard : int, default=0\n Number of Picard iterations before switching to Newton.\n Picard iterations use a simplified Jacobian and can help\n convergence for strongly nonlinear problems.\n verbose : bool, default=False\n Print solver progress and timing information.\n debug : bool, default=False\n Enable debug output including residual norms.\n debug_name : str, optional\n Name prefix for debug output files.\n _force_setup : bool, default=False\n Force rebuild of the solver even if already set up.\n time : float or Quantity, optional\n Physical time for this solve. Passed as ``petsc_t`` to all\n pointwise residual and Jacobian functions. Expressions using\n ``mesh.t`` will evaluate at this time. Non-dimensionalised\n automatically when scaling is active. Default: None (petsc_t=0).\n divergence_retries : int, default=0\n If the final SNES solve reports DIVERGED, re-call it with warm\n start up to this many times. A single retry rescues most VEP\n yield-surface kink divergences. 0 preserves legacy behaviour.\n\n Returns\n -------\n None\n Solution stored in ``self.u`` (velocity) and ``self.p`` (pressure).\n\n Examples\n --------\n >>> # Basic Stokes solve\n >>> stokes.solve()\n >>> velocity = stokes.u.array[:, 0, :]\n >>> pressure = stokes.p.array[:, 0, 0]\n\n >>> # Nonlinear solve with Picard warmup\n >>> stokes.solve(picard=3)\n\n >>> # Time-stepping with previous solution\n >>> for step in range(n_steps):\n ... # Update boundary conditions, material properties...\n ... stokes.solve(zero_init_guess=False)\n\n Notes\n -----\n This is a **collective operation** - all MPI ranks must call it.\n\n For nonlinear viscosity (e.g., power-law, viscoplastic), the solver\n uses Newton iteration by default. The ``picard`` parameter can help\n with initial convergence by using simpler Jacobian approximations.\n\n See Also\n --------\n u : Velocity solution variable.\n p : Pressure solution variable.\n constitutive_model : Viscosity and stress definitions.\n", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SNES_NavierStokes", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "DFDt", - "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2955, - "signature": "(self)", + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 8348, + "signature": "def estimate_dt(self):", "parameters": [], "returns": null, - "existing_docstring": "Time derivative operator for stress flux.", + "existing_docstring": "\n Calculates an appropriate advective timestep for the given\n mesh and velocity configuration.\n", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": null, "is_public": true }, { - "name": "constraints", + "name": "F0", "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2960, + "file": "src/underworld3/systems/solver_template.py", + "line": 136, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Constraint equation (typically incompressibility).", - "harvested_comments": [], + "existing_docstring": "Pointwise source/sink term :math:`f_0(u)`.\n\nThis represents the volumetric source term in the weak form.", + "harvested_comments": [ + "Note: negative sign for weak form" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "constraints", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2965, - "signature": "(self, constraints_matrix)", - "parameters": [ - { - "name": "constraints_matrix", - "type_hint": null, - "default": null, - "description": "" - } + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solver_template.py", + "line": 151, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Flux term :math:`\\mathbf{F}_1(u, \\nabla u)`.\n\nThis represents the flux that appears in the divergence term.", + "harvested_comments": [ + "Default: simple diffusion" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "SNES_MyEquation", + "is_public": true + }, + { + "name": "my_equation_problem_description", + "kind": "method", + "file": "src/underworld3/systems/solver_template.py", + "line": 172, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Set the constraint equation.", - "harvested_comments": [], + "existing_docstring": "Set up the problem description for the PETSc solver.\n\nThis method defines the residual terms for the finite element formulation.", + "harvested_comments": [ + "Source term (f0 - pointwise integration)", + "Flux term (f1 - integration by parts)" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "bodyforce", + "name": "f", "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2972, + "file": "src/underworld3/systems/solver_template.py", + "line": 188, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Body force vector (e.g., gravity).", + "existing_docstring": "Source term function.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "bodyforce", + "name": "f", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2977, + "file": "src/underworld3/systems/solver_template.py", + "line": 193, "signature": "(self, value)", "parameters": [ { @@ -4941,37 +4660,37 @@ } ], "returns": null, - "existing_docstring": "Set the body force vector.", + "existing_docstring": "Set the source term.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "saddle_preconditioner", + "name": "alpha", "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2983, + "file": "src/underworld3/systems/solver_template.py", + "line": 199, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Preconditioner for the Schur complement.", + "existing_docstring": "Example parameter alpha.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "saddle_preconditioner", + "name": "alpha", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2988, + "file": "src/underworld3/systems/solver_template.py", + "line": 204, "signature": "(self, value)", "parameters": [ { @@ -4982,37 +4701,37 @@ } ], "returns": null, - "existing_docstring": "Set the Schur complement preconditioner.", + "existing_docstring": "Set parameter alpha.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "penalty", + "name": "beta", "kind": "property", - "file": "src/underworld3/systems/solvers.py", - "line": 2995, + "file": "src/underworld3/systems/solver_template.py", + "line": 210, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Augmented Lagrangian penalty parameter.", + "existing_docstring": "Example parameter beta.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "penalty", + "name": "beta", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 3000, + "file": "src/underworld3/systems/solver_template.py", + "line": 215, "signature": "(self, value)", "parameters": [ { @@ -5023,2738 +4742,2564 @@ } ], "returns": null, - "existing_docstring": "Set the augmented Lagrangian penalty parameter.", + "existing_docstring": "Set parameter beta.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "solve", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 3006, - "signature": "(self, zero_init_guess: bool = True, timestep: float = None, _force_setup: bool = False, verbose = False, _evalf = False, order = None)", - "parameters": [ - { - "name": "zero_init_guess", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "timestep", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "_force_setup", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "_evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "order", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "name": "constitutive_model", + "kind": "property", + "file": "src/underworld3/systems/solver_template.py", + "line": 221, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.", - "harvested_comments": [ - "this will force an initialisation because the functions need to be updated", - "Update SemiLagrange Flux terms" - ], - "status": "partial", + "existing_docstring": "Constitutive model defining material response.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "estimate_dt", + "name": "constitutive_model", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 3072, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Estimate an appropriate timestep for the Navier-Stokes solver.\n\nThis is an implicit solver, so the returned :math:`\\delta t` should\nbe interpreted as:\n\n- :math:`\\delta t_{\\textrm{diff}}`: typical time for vorticity diffusion across an element\n- :math:`\\delta t_{\\textrm{adv}}`: typical element-crossing time for a fluid parcel\n\nThe Navier-Stokes equations include momentum diffusion via kinematic\nviscosity :math:`\\nu = \\eta/\\rho`, so the diffusive timestep is computed\nfrom this quantity.\n\nReturns\n-------\ntuple\n (:math:`\\delta t_{\\textrm{diff}}`, :math:`\\delta t_{\\textrm{adv}}`)", - "harvested_comments": [ - "## required modules", - "For Navier-Stokes, diffusivity is the kinematic viscosity: \u03bd = \u03b7/\u03c1", - "Use the unified .K property from the constitutive model (returns viscosity)", - "Evaluate the viscosity (handles constant and spatially-varying cases)", - "If diffusivity is unit-aware (UnitAwareArray), nondimensionalise it to get" + "file": "src/underworld3/systems/solver_template.py", + "line": 226, + "signature": "(self, model)", + "parameters": [ + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "partial", + "returns": null, + "existing_docstring": "Set the constitutive model.", + "harvested_comments": [], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "view", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 94, - "signature": "()", + "name": "CM_is_setup", + "kind": "property", + "file": "src/underworld3/systems/solver_template.py", + "line": 232, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Check if constitutive model is set up.", "harvested_comments": [ - "# Docstring (static)", - "docstring = docstring.replace(\"$\", \"$\").replace(\"$\", \"$\")" + "No model needed" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "mesh_adapt_meshVar", - "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 508, - "signature": "(mesh, meshVarH, metricVar, verbose = False, redistribute = False)", + "name": "solve", + "kind": "method", + "file": "src/underworld3/systems/solver_template.py", + "line": 240, + "signature": "(self, verbose = False, debug = False)", "parameters": [ { - "name": "mesh", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "meshVarH", + "name": "verbose", "type_hint": null, - "default": null, + "default": "False", "description": "" }, { - "name": "metricVar", + "name": "debug", "type_hint": null, - "default": null, + "default": "False", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Solve the equation system.\n\nParameters\n----------\nverbose : bool\n Enable verbose solver output\ndebug : bool\n Enable debug output\n\nReturns\n-------\nConvergence information from PETSc solver\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.", + "harvested_comments": [ + "Set up problem if needed", + "Call parent solve method" + ], + "status": "complete", + "needs": [], + "parent_class": "SNES_MyEquation", + "is_public": true + }, + { + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/systems/solver_template.py", + "line": 266, + "signature": "(self, dt_min = 1e-15, dt_max = 1.0)", + "parameters": [ { - "name": "verbose", + "name": "dt_min", "type_hint": null, - "default": "False", + "default": "1e-15", "description": "" }, { - "name": "redistribute", + "name": "dt_max", "type_hint": null, - "default": "False", + "default": "1.0", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Estimate appropriate timestep for stability.\n\nParameters\n----------\ndt_min : float\n Minimum allowed timestep\ndt_max : float\n Maximum allowed timestep\n\nReturns\n-------\nfloat\n Estimated timestep", "harvested_comments": [ - "Create / use a field on the old mesh to hold the metric", - "Perhaps that should be a user-definition" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "Get characteristic length scale", + "Example: CFL-type condition", + "dt < h^2 / (2 * diffusivity) for diffusion", + "Extract diffusivity parameter (problem-specific)", + "Additional stability constraints" ], - "parent_class": null, + "status": "complete", + "needs": [], + "parent_class": "SNES_MyEquation", "is_public": true }, { - "name": "n", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 86, - "signature": "def n(self):", + "name": "F0", + "kind": "property", + "file": "src/underworld3/systems/solver_template.py", + "line": 354, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Vector pointwise source term.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MyVectorEquation", "is_public": true }, { - "name": "ndim", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 90, - "signature": "def ndim(self):", + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solver_template.py", + "line": 364, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Vector flux term - typically a tensor.", + "harvested_comments": [ + "Default: identity tensor (for demonstration)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MyVectorEquation", "is_public": true }, { - "name": "kdtree_points", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 160, - "signature": "def kdtree_points(self):", + "name": "f", + "kind": "property", + "file": "src/underworld3/systems/solver_template.py", + "line": 377, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Vector source term.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MyVectorEquation", "is_public": true }, { - "name": "find_closest_point", + "name": "f", "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 170, - "signature": "def find_closest_point(self,\n const double[:,::1] coords not None: numpy.ndarray):", - "parameters": [], + "file": "src/underworld3/systems/solver_template.py", + "line": 382, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set vector source term.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MyVectorEquation", "is_public": true }, { - "name": "find_closest_n_points", + "name": "poisson_problem_description", "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 215, - "signature": "def find_closest_n_points(self,\n const int nCount : numpy.int64,\n const double[: ,::1] coords not None: numpy.ndarray):", + "file": "src/underworld3/systems/solvers.py", + "line": 261, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Build residual terms for Poisson FEM assembly.", + "harvested_comments": [ + "f1 residual term (weighted integration) - scalar function", + "f1 residual term (integration by parts / gradients)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Poisson", "is_public": true }, { - "name": "query", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", + "name": "f", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", "line": 273, - "signature": "def query(self,\n coords,\n k=1,\n sqr_dists=True,\n ):", + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Source term for the Poisson equation.\n\nThe source term :math:`f` appears on the right-hand side:\n\n.. math::\n - \\nabla \\cdot (\\kappa \\nabla u) = f\n\nReturns\n-------\nsympy.Matrix\n Source term expression (scalar, shape ``(1, 1)``).\n\nSee Also\n--------\nconstitutive_model : Provides the diffusivity :math:`\\kappa`.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Poisson", "is_public": true }, { - "name": "rbf_interpolator_local", + "name": "f", "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 336, - "signature": "def rbf_interpolator_local(self,\n coords,\n data,\n nnn = 4,\n p=2,\n verbose = False,\n ):", - "parameters": [], + "file": "src/underworld3/systems/solvers.py", + "line": 293, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Set the source term (handles units and scaling).", + "harvested_comments": [ + "Handle UWQuantity with units - enforce \"units everywhere\" principle", + "Extract the plain value", + "If ND scaling is active, scale the constant", + "The source term should have same dimensionality as the unknown field", + "Access via self.Unknowns.u (Poisson) or self.Unknowns.DuDt.u (Stokes)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Poisson", "is_public": true }, { - "name": "old_rbf_interpolator_local_from_kdtree", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 348, - "signature": "def old_rbf_interpolator_local_from_kdtree(self,\n coords,\n data,\n nnn = 4,\n verbose = False,\n ):", + "name": "CM_is_setup", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 355, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Whether the constitutive model is configured for this solver.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Poisson", "is_public": true }, { - "name": "old_rbf_interpolator_local_to_kdtree", + "name": "darcy_problem_description", "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 409, - "signature": "def old_rbf_interpolator_local_to_kdtree(self,\n coords,\n data,\n nnn = 4,\n verbose = False,\n weights = None\n ):", + "file": "src/underworld3/systems/solvers.py", + "line": 481, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Build residual terms for Darcy flow FEM assembly.", + "harvested_comments": [ + "f1 residual term (weighted integration)", + "f1 residual term (integration by parts / gradients)", + "Flow calculation" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "rbf_interpolator_local_from_kdtree", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 476, - "signature": "def rbf_interpolator_local_from_kdtree(self, coords, data, nnn, p, verbose):", + "name": "f", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 495, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Source term for the Darcy equation.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "k", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 111, - "signature": "(inner_self)", + "name": "f", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 500, + "signature": "(self, value)", "parameters": [ { - "name": "inner_self", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the source term.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "k", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 115, - "signature": "(inner_self, value)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "darcy_flux", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 506, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Darcy flux velocity computed from pressure gradient.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "solver", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 150, - "signature": "(self, solver_object)", - "parameters": [ - { - "name": "solver_object", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "v", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 512, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Projected Darcy velocity field.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 300, - "signature": "(inner_self)", + "name": "v", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 517, + "signature": "(self, value)", "parameters": [ { - "name": "inner_self", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the velocity projection target.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "viscosity", + "name": "solve", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 304, - "signature": "(inner_self, value: Union[float, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 523, + "signature": "(self, zero_init_guess: bool = True, timestep: float = None, verbose: bool = False, _force_setup: bool = False, divergence_retries: int = 0)", "parameters": [ { - "name": "inner_self", - "type_hint": null, - "default": null, + "name": "zero_init_guess", + "type_hint": "bool", + "default": "True", "description": "" }, { - "name": "value", - "type_hint": "Union[float, sympy.Function]", - "default": null, + "name": "timestep", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "_force_setup", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "divergence_retries", + "type_hint": "int", + "default": "0", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Solve the Darcy flow system.\n\nComputes the pressure field and Darcy flux velocity.\n\nParameters\n----------\nzero_init_guess : bool, optional\n If True (default), start from zero initial guess.\n If False, use current field values as initial guess.\ntimestep : float, optional\n Timestep value for inertial terms (if applicable).\nverbose : bool, optional\n If True, print solver progress information.\n_force_setup : bool, optional\n Force re-setup of solver even if already configured.\ndivergence_retries : int, optional\n If SNES reports DIVERGED, retry with warm start up to this\n many times. 0 preserves legacy behaviour.\n\nNotes\n-----\nAfter solving, the pressure field ``self.u`` and velocity field\n``self.v`` contain the solution.", + "harvested_comments": [ + "Solve pressure", + "Now solve flow field: v = -flux = -K(grad(h) - s)", + "self._v_projector.petsc_options[\"snes_rtol\"] = 1.0e-6", + "self._v_projector.petsc_options.delValue(\"ksp_monitor\")" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Darcy", "is_public": true }, { - "name": "shear_viscosity_0", + "name": "storage", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 434, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 685, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Specific storage coefficient :math:`S_s`.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "shear_viscosity_0", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 438, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "delta_t", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 695, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Current timestep.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "shear_viscosity_min", + "name": "F0", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 443, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 720, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Pointwise storage + source: :math:`S_s \\dot{h} / \\Delta t - f`.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "shear_viscosity_min", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 447, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 731, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Pointwise flux: Adams-Moulton time-weighted Darcy flux.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "shear_viscosity_max", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 452, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 744, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Estimate a stable timestep based on diffusive CFL.\n\nReturns\n-------\nfloat or pint.Quantity\n Diffusive timestep :math:`\\delta t = (\\Delta x)^2 / K_{\\max}`.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "shear_viscosity_max", + "name": "solve", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 456, - "signature": "(inner_self, value: Union[list, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 793, + "signature": "(self, zero_init_guess: bool = True, timestep = None, _force_setup: bool = False, verbose = False, divergence_retries: int = 0)", "parameters": [ { - "name": "inner_self", + "name": "zero_init_guess", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "timestep", "type_hint": null, - "default": null, + "default": "None", "description": "" }, { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, + "name": "_force_setup", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "divergence_retries", + "type_hint": "int", + "default": "0", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Solve the transient Darcy system for one timestep.\n\nParameters\n----------\nzero_init_guess : bool, optional\n Start from zero initial guess (default True).\ntimestep : float, optional\n Timestep size. Updates ``self.delta_t`` if provided.\n_force_setup : bool, optional\n Force re-setup of solver.\nverbose : bool, optional\n Print solver progress.\ndivergence_retries : int, optional\n If SNES reports DIVERGED, retry with warm start up to this\n many times. 0 preserves legacy behaviour.", + "harvested_comments": [ + "Pre-solve: update history terms", + "Solve PDE (bypass SNES_Darcy.solve to avoid double setup/projection)", + "Invalidate cached data views", + "Post-solve: shift history", + "Velocity projection (inherited from Darcy). The physical Darcy" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_TransientDarcy", "is_public": true }, { - "name": "yield_stress", + "name": "water_content", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 461, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 969, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Water content function :math:`\\theta(\\psi)` for the mixed form.\n\nWhen set, the storage term uses\n:math:`(\\theta(\\psi^{n+1}) - \\theta(\\psi^n)) / \\Delta t`\ninstead of :math:`C(\\psi) \\cdot (\\psi^{n+1} - \\psi^n) / \\Delta t`,\ngiving exact mass conservation (Celia et al., 1990).\n\nThe expression should be a SymPy function of ``psi.sym[0]``.\nThe Jacobian :math:`\\partial\\theta/\\partial\\psi = C(\\psi)` is\ncomputed automatically by PETSc (finite-difference colouring),\nso there is no need to provide :math:`C(\\psi)` separately.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Richards", "is_public": true }, { - "name": "yield_stress", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 465, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "capacity", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 990, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Specific moisture capacity :math:`C(\\psi) = d\\theta/d\\psi`.\n\nUsed only when ``water_content`` is None (head-based form).\nTypically a nonlinear SymPy expression depending on ``psi.sym[0]``.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Richards", "is_public": true }, { - "name": "yield_stress_min", + "name": "psi", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 470, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1004, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Alias for ``self.u`` (pressure head).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Richards", "is_public": true }, { - "name": "yield_stress_min", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 474, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "F0", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 1009, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise storage + source term.\n\nMixed form: :math:`(\\theta(\\psi^{n+1}) - \\theta(\\psi^n)) / \\Delta t - f`\n\nHead-based form: :math:`C(\\psi) (\\psi^{n+1} - \\psi^n) / \\Delta t - f`", + "harvested_comments": [ + "Mixed (mass-conservative) form:", + "\u03b8(\u03c8^{n+1}) is self._water_content (already in terms of psi.sym[0])", + "\u03b8(\u03c8^n) is water_content with psi.sym[0] \u2192 psi_star[0].sym[0]", + "Head-based form (backward compatible)" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Richards", "is_public": true }, { - "name": "strainrate_inv_II", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 479, - "signature": "(inner_self)", + "name": "set_jacobian_F1_source", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 1183, + "signature": "(self, F1_source, linesearch = 'cp')", "parameters": [ { - "name": "inner_self", + "name": "F1_source", "type_hint": null, "default": null, "description": "" + }, + { + "name": "linesearch", + "type_hint": null, + "default": "'cp'", + "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Override the F1 expression used to build the Jacobian blocks.\n\nBy default, the Stokes Jacobian's uu / up G2, G3 blocks are\nautodiff'd from the residual F1. Some problems benefit from\ndifferentiating a *different* but related expression \u2014 e.g. a\nsmooth (softmin) viscosity formula for the Jacobian while the\nresidual F1 keeps a sharp Min, so Newton sees a continuous\nderivative even when the iterate sits exactly on the yield kink.\n\nSetting ``F1_source`` triggers a JIT recompile (the Jacobian\nsymbols change). Pass ``None`` to revert to autodiff of F1.\n\nParameters\n----------\nF1_source : sympy.Matrix or None\n Alternative expression of the same shape as ``F1.sym``.\nlinesearch : str or None, default ``\"cp\"``\n SNES linesearch type to install when ``F1_source`` is set.\n Defaults to ``\"cp\"`` (critical-point) because inexact-Newton\n steps don't reliably reduce the residual norm and PETSc's\n default ``bt`` (backtracking) consequently rejects useful\n steps with ``DIVERGED_LINE_SEARCH``. ``cp`` accepts the\n predicted step at the optimum of the local linearisation and\n converges cleanly on the same problems where ``bt`` flails.\n Set to ``None`` to leave the linesearch type untouched (e.g.\n if you've already configured one via ``petsc_options``).\n Has no effect when ``F1_source is None``.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "strainrate_inv_II", + "name": "solve", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 483, - "signature": "(inner_self, value: sympy.Function)", + "file": "src/underworld3/systems/solvers.py", + "line": 1261, + "signature": "(self, zero_init_guess: bool = True, timestep: float = None, _force_setup: bool = False, verbose: bool = False, debug: bool = False, debug_name: str = None, evalf = False, order = None, picard: int = 0, divergence_retries: int = 0)", "parameters": [ { - "name": "inner_self", + "name": "zero_init_guess", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "timestep", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "_force_setup", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "debug", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "debug_name", + "type_hint": "str", + "default": "None", + "description": "" + }, + { + "name": "evalf", "type_hint": null, - "default": null, + "default": "False", "description": "" }, { - "name": "value", - "type_hint": "sympy.Function", - "default": null, + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "picard", + "type_hint": "int", + "default": "0", + "description": "" + }, + { + "name": "divergence_retries", + "type_hint": "int", + "default": "0", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Solve the Stokes system, with optional viscoelastic stress history.\n\nWhen a constitutive model with stress history is active (DFDt is not None),\nthe solve includes pre/post hooks for advecting stress history, updating\nBDF coefficients, and projecting the actual stress after the solve.\n\nParameters\n----------\nzero_init_guess : bool\n If True, use zero initial guess. Otherwise use current field values.\ntimestep : float, optional\n Advection timestep. Required when stress history is active.\n_force_setup : bool\n Force rebuild of pointwise functions.\nverbose : bool\n Enable verbose output.\nevalf : bool\n Force numerical evaluation during history updates.\norder : int, optional\n Override the VE time integration order.\npicard : int, default=0\n Number of Picard iterations before switching to Newton.\n Picard uses a simplified Jacobian and can help convergence\n for strongly nonlinear problems like VEP at yield onset.\ndivergence_retries : int, default=0\n If SNES returns a DIVERGED reason after the main solve, re-call\n the underlying Newton up to this many times with a warm start\n (``zero_init_guess=False``) to try to rescue. Each retry uses\n the just-computed iterate plus the freshly-advected stress\n history, which is often enough for VEP at yield onset (Min/softmin\n kinks) to step off a bad Newton iterate. ``0`` preserves legacy\n behaviour (divergence is terminal). Typical useful value is 1.\n Only applies in the VE/VEP branch (``DFDt is not None``).", + "harvested_comments": [ + "dt_elastic must always equal the solve timestep. The constitutive", + "model's VE formulas (eta_eff, stress history terms) all reference", + "Parameters.dt_elastic. If it differs from the actual timestep,", + "the stress computation is inconsistent with the time integration.", + "Re-setup when effective_order changes (DDt history ramp-up)" ], - "parent_class": "_Parameters", + "status": "complete", + "needs": [], + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "strainrate_inv_II_min", + "name": "tau", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 488, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1432, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Deviatoric stress from the most recent solve.\n\nWhen stress history is active (VEP), returns ``psi_star[0]`` which\ncontains the actual projected stress. Otherwise falls through to the\nbase class lazy projection.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "strainrate_inv_II_min", + "name": "stokes_problem_description", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 492, - "signature": "(inner_self, value: float)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "float", - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1491, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Build residual terms for Stokes FEM assembly (deprecated).", + "harvested_comments": [ + "f0 residual term", + "f1 residual term", + "p0 residual term" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "averaging_method", + "name": "CM_is_setup", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 497, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1505, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Whether the constitutive model is configured for this solver.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "averaging_method", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 501, - "signature": "(inner_self, value: str)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "name": "strainrate", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 1510, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Symmetric strain rate tensor from velocity gradients.\n\nThe strain rate tensor :math:`\\dot{\\varepsilon}` is computed as:\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)\n\nReturns\n-------\nsympy.Matrix\n Symmetric tensor of shape ``(dim, dim)`` where ``dim`` is the\n mesh dimensionality.\n\nSee Also\n--------\nstrainrate_1d : Voigt notation (vector) form.\nstress_deviator : Deviatoric stress computed from strain rate.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "materialIndex", + "name": "strainrate_1d", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 507, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1533, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Strain rate in Voigt notation (vector form).\n\nConverts the symmetric strain rate tensor to Voigt notation for\nuse in constitutive model calculations. In 2D, returns a 3-component\nvector; in 3D, a 6-component vector.\n\nReturns\n-------\nsympy.Matrix\n Strain rate in Voigt notation.\n\nSee Also\n--------\nstrainrate : Full tensor form.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "materialIndex", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 511, - "signature": "(inner_self, indexVar)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "indexVar", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "strainrate_star_1d", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 1552, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "error checking, only support IndexSwarmVariables for now" - ], - "status": "none", + "existing_docstring": "Historical strain rate in Voigt notation (for viscoelastic models).\n\nUsed in viscoelastic formulations where the stress depends on\nboth current and historical strain rates.\n\nReturns\n-------\nsympy.Matrix\n Historical strain rate in Voigt notation.\n\nSee Also\n--------\nstrainrate_1d : Current strain rate.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "plastic_eff_viscosity", + "name": "stress_deviator", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 519, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1570, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "## creates list of values that has the same length as the material index" - ], - "status": "none", + "existing_docstring": "Deviatoric stress tensor from the constitutive model.\n\nThe deviatoric stress :math:`\\boldsymbol{\\tau}` is the traceless part\nof the stress tensor, computed by the constitutive model from the\nstrain rate:\n\n.. math::\n \\boldsymbol{\\tau} = \\boldsymbol{\\eta} : \\dot{\\boldsymbol{\\varepsilon}}\n\nFor a Newtonian fluid: :math:`\\boldsymbol{\\tau} = 2\\eta\\dot{\\boldsymbol{\\varepsilon}}`\n\nReturns\n-------\nsympy.Matrix\n Deviatoric stress tensor of shape ``(dim, dim)``.\n\nSee Also\n--------\nstress : Total stress (deviatoric + pressure).\nconstitutive_model : Provides the viscosity relationship.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "viscosity", + "name": "stress_deviator_1d", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 607, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1595, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Deviatoric stress in Voigt notation.\n\nReturns\n-------\nsympy.Matrix\n Deviatoric stress in Voigt notation.\n\nSee Also\n--------\nstress_deviator : Full tensor form.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_viscosity_0", + "name": "stress", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 861, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1610, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Total Cauchy stress tensor.\n\nThe total stress combines the deviatoric stress and pressure:\n\n.. math::\n \\boldsymbol{\\sigma} = \\boldsymbol{\\tau} - p\\mathbf{I}\n\nwhere :math:`\\boldsymbol{\\tau}` is the deviatoric stress and\n:math:`p` is the pressure (positive in compression).\n\nReturns\n-------\nsympy.Matrix\n Total stress tensor of shape ``(dim, dim)``.\n\nSee Also\n--------\nstress_deviator : Deviatoric (traceless) part.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_viscosity_0", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 865, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "stress_1d", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 1633, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Total stress in Voigt notation.\n\nReturns\n-------\nsympy.Matrix\n Total stress in Voigt notation.\n\nSee Also\n--------\nstress : Full tensor form.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_modulus", + "name": "div_u", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 870, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1648, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Velocity divergence.\n\nFor incompressible flow, this should be zero:\n\n.. math::\n \\nabla \\cdot \\mathbf{u} = 0\n\nReturns\n-------\nsympy.Expr\n Scalar divergence expression.\n\nNotes\n-----\nNon-zero divergence indicates compressibility or mass sources/sinks.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_modulus", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 874, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "constraints", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 1668, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Constraint equation for the saddle-point system.\n\nBy default, this is the incompressibility constraint\n:math:`\\nabla \\cdot \\mathbf{u} = 0`. Can be modified for\ncompressible or other constrained formulations.\n\nReturns\n-------\nsympy.Expr\n Constraint expression.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "dt_elastic", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 879, - "signature": "(inner_self)", + "name": "constraints", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 1683, + "signature": "(self, constraints_matrix)", "parameters": [ { - "name": "inner_self", + "name": "constraints_matrix", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the constraint equation (e.g., incompressibility).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "dt_elastic", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 883, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "bodyforce", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 1690, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Body force vector (source term).\n\nThe volumetric body force :math:`\\mathbf{f}` appears on the\nright-hand side of the momentum equation:\n\n.. math::\n -\\nabla \\cdot \\boldsymbol{\\sigma} = \\mathbf{f}\n\nCommon examples include gravity (:math:`\\rho\\mathbf{g}`) or\nbuoyancy forces.\n\nReturns\n-------\nUWexpression\n Body force vector expression.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_viscosity_min", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 888, - "signature": "(inner_self)", + "name": "bodyforce", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 1710, + "signature": "(self, value)", "parameters": [ { - "name": "inner_self", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Set the body force vector (e.g., gravity, buoyancy).", + "harvested_comments": [ + "Convert UWQuantity components to their NON-DIMENSIONAL value before", + "Matrix creation \u2014 the body force feeds the non-dimensional solver", + "(see the ND<->units boundary contract / #282). The component may be", + "symbolic (e.g. a buoyancy \u03c1\u03b1 g (T \u2212 T_ref) carries the T field), so", + "non_dimensionalise yields a (1\u00d71) array/Matrix whose element we" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_viscosity_min", + "name": "set_pressure_gauge", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 892, - "signature": "(inner_self, value: Union[list, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 1738, + "signature": "(self, boundary, reference = 0.0)", "parameters": [ { - "name": "inner_self", + "name": "boundary", "type_hint": null, "default": null, "description": "" }, { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, + "name": "reference", + "type_hint": null, + "default": "0.0", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Pin the pressure gauge by removing the surface-mean pressure each iteration.\n\nOn enclosed / all-Dirichlet-velocity problems the pressure is determined\nonly up to an additive constant (a constant null space). This registers a\nper-iteration callback (see :meth:`add_update_callback`) that subtracts the\nmean pressure over ``boundary`` from the whole pressure field at every\nnonlinear iteration, fixing a *specific, physical* gauge:\n\n.. math:: \\frac{1}{|\\Gamma|}\\int_\\Gamma p \\, dS = \\texttt{reference}\n\ne.g. ``stokes.set_pressure_gauge(\"Top\")`` makes the mean pressure on the\ntop surface zero. This is an alternative (or complement) to a constant\npressure null space when you want the gauge tied to a surface rather than\nto an arbitrary constant chosen by the solver.\n\nParameters\n----------\nboundary : str\n Mesh boundary label over which the mean pressure is fixed.\nreference : float, default 0.0\n Target mean pressure on ``boundary``.\n\nReturns\n-------\nthe registered callback (so it can be identified/removed later).", "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "_Parameters", + "status": "complete", + "needs": [], + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_viscosity_max", + "name": "saddle_preconditioner", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 897, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1776, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Pressure Schur-complement preconditioner \u2014 **usually leave unset**.\n\nApproximates the Schur complement\n:math:`\\mathbf{S} \\approx \\mathbf{B}\\mathbf{A}^{-1}\\mathbf{B}^T \\sim 1/\\eta`\n(inverse viscosity). When this is unset (the default, ``None``) the solver\nautomatically uses :math:`1/\\eta = 1/`\\ ``constitutive_model.K`` \u2014 the\ncorrect local-viscosity scaling for any constitutive model. Setting it\nexplicitly to ``1/viscosity`` is therefore **redundant** (and only a chance\nto supply an inconsistent viscosity); just leave it at the default.\n\nReturns\n-------\nsympy.Expr or None\n Preconditioner expression, or ``None`` to use the automatic\n ``1/constitutive_model.K``.\n\nNotes\n-----\nThis is an **advanced override**, useful only when the automatic\n``1/K`` is not the right Schur scaling \u2014 e.g. an anisotropic/tensorial\n``K``, or deliberate preconditioner tuning. (``Stokes_Constrained`` builds\nits Schur preconditioner automatically and does not expose this property.)", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "shear_viscosity_max", + "name": "saddle_preconditioner", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 901, - "signature": "(inner_self, value: Union[list, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 1803, + "signature": "(self, value)", "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, { "name": "value", - "type_hint": "Union[list, sympy.Function]", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the Schur complement preconditioner.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "yield_stress", + "name": "penalty", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 906, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 1810, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Augmented Lagrangian penalty parameter (dimensionless, viscosity-scaled).\n\nThe penalty adds a **viscosity-weighted** grad-div term to the weak form\nthat penalizes non-zero divergence:\n\n.. math::\n \\lambda \\int \\mu\\,(\\nabla \\cdot \\mathbf{u})(\\nabla \\cdot \\mathbf{v}) \\, dV\n\nwhere :math:`\\mu` is the local viscosity (``constitutive_model.K``). The\n:math:`\\mu`-weighting keeps the effective penalty proportional to the\nlocal stress scale, so the ratio penalty/:math:`\\mu` is uniform across a\nspatially-variable viscosity field. (A bare *constant* penalty would be\nhuge relative to the stress in low-:math:`\\mu` regions and negligible in\nhigh-:math:`\\mu` regions \u2014 over-stiffening the former into velocity\nlocking. See the design note ``CONSTRAINED_FREESLIP_MULTIPLIER``.) So the\nparameter here is a **dimensionless** base of :math:`O(1)`.\n\nReturns\n-------\nUWexpression\n Dimensionless augmented-Lagrangian penalty base (typically :math:`O(1)`).\n\nNotes\n-----\nSet to zero (the default) for a standard saddle-point Stokes solve; the\n``saddle_preconditioner`` already conditions the pressure Schur, so the\npenalty is usually unnecessary for convergence.\n\n**Pressure correction.** This term is part of the *operator*, so when it\nis non-zero the recovered pressure ``p`` is the Lagrange multiplier, not\nthe mechanical pressure. The total isotropic stress is\n:math:`-p + \\lambda\\,\\mu\\,(\\nabla\\cdot\\mathbf{u})`, so the mechanical\npressure is\n\n.. math::\n p_\\text{mech} = p - \\lambda\\,\\mu\\,(\\nabla\\cdot\\mathbf{u}).\n\nAt convergence :math:`\\nabla\\cdot\\mathbf{u}\\to 0` so the two agree, but\n*pointwise* they differ by the penalty term (\u2248 a couple of percent of\n:math:`|p|` at :math:`\\lambda=O(1)`). For a pressure-dependent\nconstitutive law (yield, density, rheology), use :math:`p_\\text{mech}`;\nfor visualisation or weak pressure dependence the raw ``p`` is adequate.\n\nReferences\n----------\nGlowinski, R., & Le Tallec, P. (1989). *Augmented Lagrangian and\nOperator-Splitting Methods in Nonlinear Mechanics*. SIAM.\nhttps://doi.org/10.1137/1.9781611970838", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "yield_stress", + "name": "penalty", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 910, - "signature": "(inner_self, value: Union[list, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 1863, + "signature": "(self, value)", "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, { "name": "value", - "type_hint": "Union[list, sympy.Function]", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the augmented Lagrangian penalty parameter.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "yield_stress_min", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 915, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 1878, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Calculates an appropriate advective timestep for the Stokes solver.\n\nThe Stokes equations are quasi-static (no time derivative \u2202v/\u2202t),\nso there is no diffusive CFL constraint. The only relevant timescale\nis the advective one: how long it takes material to cross an element.\n\nThis method computes a per-element timestep:\n\n.. math::\n \\delta t_i = h_i / \\|\\mathbf{v}_i\\|\n\nwhere $h_i$ is the element radius and $\\mathbf{v}_i$ is the velocity at the element\ncentroid, then returns the global minimum. This is more accurate than\nusing global max velocity with global min element size, especially for\nnon-uniform meshes with spatially varying velocity.\n\nReturns:\n Pint Quantity or float: The advective timestep with physical time units\n if a model with reference scales is available, otherwise nondimensional.", + "harvested_comments": [ + "Evaluate velocity at element centroids (consistent with AdvDiff)", + "If vel is unit-aware (UnitAwareArray), nondimensionalise it to get", + "consistent nondimensional values that match mesh._radii", + "Note: .magnitude returns physical units, which would be wrong here", + "Plain UWQuantity without units context - use magnitude" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes", "is_public": true }, { - "name": "yield_stress_min", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 919, - "signature": "(inner_self, value: Union[list, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[list, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "delta_t", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 2036, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Elastic timestep from the constitutive model.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_VE_Stokes", "is_public": true }, { - "name": "strainrate_inv_II", + "name": "tolerance", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 924, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 2221, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Solver tolerance (see :class:`SNES_Stokes_SaddlePt.tolerance`).\n\nOverridden so that, in addition to ``snes_rtol`` / ``ksp_rtol`` /\n``ksp_atol``, the Eisenstat-Walker initial and max relative tolerances are\npinned to ``tolerance * 0.1`` \u2014 otherwise EW's default (0.3) under-solves\nthe ill-conditioned augmented constrained system on a linear solve and the\nvelocity becomes partition-dependent (see ``__init__``).", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "strainrate_inv_II", + "name": "solve", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 928, - "signature": "(inner_self, value: sympy.Function)", + "file": "src/underworld3/systems/solvers.py", + "line": 2241, + "signature": "(self, *args, **kwargs)", "parameters": [ { - "name": "inner_self", + "name": "*args", "type_hint": null, "default": null, "description": "" }, { - "name": "value", - "type_hint": "sympy.Function", + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Solve the constrained Stokes system (see :meth:`SNES_Stokes.solve`).\n\nInstalls the guarded automatic pressure gauge (if eligible) before\ndelegating to the base solve, so the raw pressure and multiplier are\npartition-reproducible by construction on enclosed problems.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "stress_star", + "name": "saddle_preconditioner", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 933, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 2325, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Not used by ``Stokes_Constrained`` \u2014 the Schur preconditioner is built\nautomatically.\n\nThe grouped :math:`[p,\\lambda]` Schur preconditioner is formed by\n``selfp`` from the operator blocks, and the pressure mass it needs is the\n``1/viscosity`` (``1/constitutive_model.K``) term supplied automatically.\nThere is nothing for the user to set; this property is inert and assigning\nto it raises. (The base :class:`SNES_Stokes` keeps a settable\n``saddle_preconditioner`` as an advanced override.)", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "stress_star", + "name": "add_constraint_bc", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 937, - "signature": "(inner_self, value: sympy.Function)", + "file": "src/underworld3/systems/solvers.py", + "line": 2354, + "signature": "(self, conds = None, boundary = None, normal = None, screening = None, augmentation = None, augmentation_base = 10000.0, degree = None, g = None)", "parameters": [ { - "name": "inner_self", + "name": "conds", "type_hint": null, - "default": null, + "default": "None", "description": "" }, { - "name": "value", - "type_hint": "sympy.Function", - "default": null, + "name": "boundary", + "type_hint": null, + "default": "None", "description": "" - } - ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "_Parameters", - "is_public": true - }, - { - "name": "stress_star_star", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 942, - "signature": "(inner_self)", - "parameters": [ + }, { - "name": "inner_self", + "name": "normal", "type_hint": null, - "default": null, + "default": "None", + "description": "" + }, + { + "name": "screening", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "augmentation", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "augmentation_base", + "type_hint": null, + "default": "10000.0", + "description": "" + }, + { + "name": "degree", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "g", + "type_hint": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Register a multiplier-enforced normal-velocity constraint on ``boundary``.\n\nAdds a scalar multiplier field ``h`` coupled into the saddle-point system\nso that :math:`\\mathbf{u}\\cdot\\mathbf{n}=\\mathrm{conds}` is enforced on\n``boundary`` in the coupled solve; at convergence ``h`` on the boundary\nis the normal traction (dynamic topography), recoverable via\n:meth:`multiplier` / :meth:`topography`.\n\nParameters\n----------\nconds : float or sympy expression, optional\n Prescribed normal velocity :math:`\\mathbf{u}\\cdot\\mathbf{n}`,\n in the canonical value-first BC order. Default ``None`` means zero\n (free-slip).\nboundary : str\n Mesh boundary label (e.g. ``\"Upper\"``).\nnormal : sympy matrix, optional\n Row-vector constraint normal. Defaults to ``mesh.Gamma_P1``.\nscreening : float or sympy expression, optional\n Interior screening coefficient :math:`\\varepsilon` (de-singularises\n the interior multiplier DOFs). Defaults to ``1e-6``.\naugmentation : float or sympy expression, optional\n Augmented-Lagrangian parameter :math:`r`. Adds a penalty\n :math:`r(\\mathbf{n}\\cdot\\mathbf{u}-g)\\,\\mathbf{n}` to the u-row,\n giving a ``uu`` boundary stiffness :math:`r\\,(\\mathbf{n}\\otimes\n \\mathbf{n})` that conditions the :math:`[p,h]` Schur complement\n **without biasing the multiplier** (the h-row is still the exact\n constraint). Defaults to ``augmentation_base \u00b7 \u03bc(x)`` (viscosity-\n weighted, mesh-independent). Pass ``0`` for the bare KKT system.\naugmentation_base : float, default 1e4\n Base multiple used when ``augmentation`` is not given. Accuracy is\n independent of this value (the multiplier carries the exact\n constraint); larger values reduce the iteration count up to a broad\n plateau, well below the roundoff limit.\ng : float or sympy expression, optional\n Deprecated keyword alias for ``conds`` (one DeprecationWarning).\n\nReturns\n-------\nh : MeshVariable\n The scalar multiplier field.\n\nNotes\n-----\nThe legacy boundary-first call ``add_constraint_bc(boundary, g=...)``\nis detected conservatively (first positional argument a string while\nthe second is not \u2014 a BC datum is never a string) and shimmed with one\nDeprecationWarning; see\n``SolverBaseClass._value_first_bc_args``.", + "harvested_comments": [ + "Parallel-safe: the interior-multiplier reduction", + "(_constrain_interior_multipliers_in_section) is rank-local section", + "surgery (it uses the distributed boundary label IS and iterates the", + "local chart), so the global system \u2014 and hence the velocity solve and", + "the gauge-invariant boundary traction \u2014 are partition-independent." ], - "parent_class": "_Parameters", + "status": "complete", + "needs": [], + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "stress_star_star", + "name": "multiplier", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 946, - "signature": "(inner_self, value: sympy.Function)", + "file": "src/underworld3/systems/solvers.py", + "line": 2484, + "signature": "(self, boundary)", "parameters": [ { - "name": "inner_self", + "name": "boundary", "type_hint": null, "default": null, "description": "" - }, - { - "name": "value", - "type_hint": "sympy.Function", - "default": null, - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Return the multiplier field for ``boundary`` (None if not constrained).\n\nAfter :meth:`solve`, the multiplier's boundary trace is the normal\ntraction holding the constraint. Divide by :math:`\\Delta\\rho\\,g` for\ndynamic topography (see :meth:`topography`).", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "strainrate_inv_II_min", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 951, - "signature": "(inner_self)", + "name": "topography", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2496, + "signature": "(self, boundary, buoyancy_scale = 1.0, reference = None)", "parameters": [ { - "name": "inner_self", + "name": "boundary", "type_hint": null, "default": null, "description": "" + }, + { + "name": "buoyancy_scale", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "reference", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Dynamic topography expression :math:`h / (\\Delta\\rho\\, g)` on ``boundary``.\n\nFor an **enclosed** problem (no net normal flow through any boundary) the\nmultiplier :math:`h` is determined only up to the :math:`[p,\\lambda]` gauge\nconstant, and the solver lands on a **partition-dependent representative**\nof it \u2014 the velocity and the *deviation* of :math:`h` are unaffected, but\nthe absolute level of :math:`h` is not reproducible across ranks. For such\nproblems pass ``reference=\"mean\"`` to subtract the boundary mean and obtain\na gauge-fixed, partition-independent topography. The default\n(``reference=None``) returns the raw multiplier \u2014 correct for problems with\n**no** gauge freedom (e.g. an open boundary), where the mean of :math:`h` is\nthe physical mean traction and must NOT be removed.\n\nNote that the automatic pressure gauge (:attr:`auto_pressure_gauge`) fixes\nthe raw *pressure* level but NOT the raw *multiplier* level (the constant\nmultiplier is an independent gauge freedom). So on an enclosed problem the\nraw multiplier (``reference=None``) is still partition-dependent \u2014\n``reference=\"mean\"`` is the gauge-invariant, partition-reproducible read\nfor dynamic topography and is the recommended path.\n\nParameters\n----------\nboundary : str\n Constrained boundary label.\nbuoyancy_scale : float, default 1.0\n Divide by :math:`\\Delta\\rho\\,g` to convert traction to length.\nreference : {None, \"mean\"}, default None\n ``None`` returns the raw multiplier (correct when there is no gauge\n freedom). ``\"mean\"`` subtracts the boundary mean (gauge-fixed,\n reproducible) \u2014 use for enclosed problems.\n\nNotes\n-----\n``reference=\"mean\"`` evaluates two ``BdIntegral`` reductions immediately\nto compute the boundary mean, which are **collective** MPI operations \u2014\nin parallel it must be called on every rank (do not guard it behind a\nsingle-rank branch). ``reference=None`` is a pure symbolic accessor with\nno reduction.", + "harvested_comments": [ + "Subtract the boundary mean of h via parallel-safe surface integrals", + "(BdIntegral handles the cross-rank reduction); this fixes the gauge." ], - "parent_class": "_Parameters", + "status": "complete", + "needs": [], + "parent_class": "SNES_Stokes_Constrained", "is_public": true }, { - "name": "strainrate_inv_II_min", + "name": "linear_solver", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 955, - "signature": "(inner_self, value: float)", + "file": "src/underworld3/systems/solvers.py", + "line": 2704, + "signature": "(self, pc = 'jacobi', rtol = 1e-10)", "parameters": [ { - "name": "inner_self", + "name": "pc", "type_hint": null, - "default": null, + "default": "'jacobi'", "description": "" }, { - "name": "value", - "type_hint": "float", - "default": null, + "name": "rtol", + "type_hint": null, + "default": "1e-10", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Switch this projector to a lightweight *linear* (SPD) solve.\n\nAn L2 projection (and the screened-Poisson smoother) is a **linear,\nsymmetric-positive-definite** problem, so the inherited\n``newtonls / gmres / gamg`` default is unnecessarily heavy \u2014 GAMG\nsetup/repartition dominates cost and memory at MPI scale, which is the\nbottleneck for repeated post-processing projections (UW3 issue #156).\nThis replaces it with ``ksponly + CG + a cheap preconditioner`` \u2014 the\nright tool for the mass/Helmholtz matrix \u2014 and removes the now-unused\nGAMG options.\n\nOpt-in: the default ``SNES_Scalar`` solver stack is unchanged for code\nthat relies on it. Call this on a projector used purely for output /\npost-processing.\n\nParameters\n----------\npc : str, default \"jacobi\"\n Preconditioner. ``\"jacobi\"`` is fine for a well-conditioned mass\n matrix; use ``\"bjacobi\"`` or ``\"icc\"`` if CG iteration counts climb\n on distorted or high-degree meshes.\nrtol : float, default 1e-10\n KSP relative tolerance.\n\nReturns\n-------\nself (so the call can be chained).", + "harvested_comments": [ + "GAMG-specific options are now unused; remove them to avoid PETSc", + "\"unused option\" warnings (and any stale AMG configuration)." ], - "parent_class": "_Parameters", + "status": "complete", + "needs": [], + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "averaging_method", + "name": "smoothing", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 960, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 2752, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Smoothing coefficient :math:`\\alpha` of the screened-Poisson form.\n\nThe projection solves\n:math:`u - \\nabla\\!\\cdot\\!(\\alpha\\,\\nabla u) = \\tilde f`, so\n:math:`\\alpha` has dimensions of **length\u00b2** and the implied\nsmoothing length is :math:`L = \\sqrt{\\alpha}`. The free-space\nGreen's function decays as :math:`\\exp(-r/L)`, giving the\nprojection the action of a Gaussian-like convolution of width\n:math:`L` \u2014 without ever forming the kernel.\n\nSee the class docstring for the full derivation.\n\nTwo usage patterns\n~~~~~~~~~~~~~~~~~~\n\n* **Pure L2 projection** \u2014 ``smoothing = 0`` (or omit). No\n regularisation; ``u`` is the best L2 fit to ``\u0169``.\n* **Genuine length-scale smoother** \u2014 set\n :attr:`smoothing_length` to the desired physical length\n (unit-aware), or equivalently\n ``smoothing = L**2``. The output is ``\u0169`` smoothed at\n scale ``L``.\n\n.. note::\n\n Historical UW3 code occasionally sets ``smoothing`` to a\n tiny number (e.g. ``1e-6``) as a *numerical* regulariser\n against rank deficiency. That value corresponds to\n :math:`L \\approx 10^{-3}` in the problem's ND length\n units \u2014 almost always well below the cell size, so it\n does no useful smoothing. Use a true sub-grid value only\n if you need that regularisation; for filtering, use\n :attr:`smoothing_length`.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "averaging_method", + "name": "smoothing", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 964, - "signature": "(inner_self, value: str)", + "file": "src/underworld3/systems/solvers.py", + "line": 2790, + "signature": "(self, smoothing_factor)", "parameters": [ { - "name": "inner_self", + "name": "smoothing_factor", "type_hint": null, "default": null, "description": "" - }, - { - "name": "value", - "type_hint": "str", - "default": null, - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the smoothing regularization parameter.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "materialIndex", + "name": "smoothing_length", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 970, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 2796, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Smoothing length scale :math:`L` (length units, **unit-aware**).\n\nL-valued view on :attr:`smoothing` with the convention\n:math:`L = \\sqrt{\\alpha}`. Setting ``smoothing_length = L``\nis equivalent to setting ``smoothing = L**2``, but is the\nnatural physical knob because :math:`L` is what the\nsmoother actually filters by.\n\nMathematical meaning\n--------------------\n\nThe projection then solves the screened-Poisson equation\n\n.. math::\n\n u \\;-\\; L^{2}\\,\\nabla^{2} u \\;=\\; \\tilde f,\n\nwhose Green's function decays as :math:`\\exp(-r/L)`. In\npractice this acts like a Gaussian convolution of width\n:math:`L`:\n\n* features in :math:`\\tilde f` with spatial scale\n :math:`\\ll L` are damped (roughly as\n :math:`1/(1+k^{2}L^{2})` for a wavenumber :math:`k`);\n* features with scale :math:`\\gg L` are preserved;\n* features at :math:`\\sim L` are attenuated by a factor\n near ``1/2``.\n\nChoosing :math:`L` smaller than the local mesh size has\nessentially no effect (the field is already band-limited\nby the discretisation). A useful default for *light*\nde-noising is :math:`L \\approx 1\\!-\\!2\\,h`, where\n:math:`h` is a representative cell size.\n\nUnits\n-----\n\nThe setter accepts a plain number (assumed already\nnon-dimensional), a pint ``Quantity`` with length units,\nor any unit-aware object understood by\n:func:`underworld3.non_dimensionalise`. Internally the\nsquared non-dimensional value is stored in\n``self._smoothing`` (so ``smoothing`` and\n``smoothing_length`` stay consistent).\n\nThe getter returns a Pint ``Quantity`` with length units\nwhen a scaling context is configured; otherwise the plain\nnon-dimensional float :math:`\\sqrt{\\alpha}`.", + "harvested_comments": [ + "Return a Pint Quantity only if the user set a dimensional value via", + "the setter; plain-float input round-trips as a plain float so the", + "meaning of the number the user passed is preserved." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "materialIndex", + "name": "smoothing_length", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 974, - "signature": "(inner_self, indexVar)", + "file": "src/underworld3/systems/solvers.py", + "line": 2865, + "signature": "(self, L)", "parameters": [ { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "indexVar", + "name": "L", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the smoothing length scale.\n\nAccepts a Pint Quantity (with length units), a UnitAware\nscalar, or a plain non-dimensional number. The value is\nnon-dimensionalised through the active scaling context\nbefore being squared and stored as ``self._smoothing``.", "harvested_comments": [ - "error checking, only support IndexSwarmVariables for now" + "Unit-aware: a dimensional input (Pint Quantity / UnitAware) is", + "non-dimensionalised through the active scaling context and reduced to", + "a plain float (uw.non_dimensionalise returns a dimensionless", + "UWQuantity, which sympify can't square); a plain number is taken as", + "already non-dimensional." ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "t_relax", + "name": "uw_weighting_function", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 981, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 2891, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "shear modulus defaults to infinity so t_relax goes to zero", - "in the viscous limit" - ], - "status": "none", + "existing_docstring": "Weighting function applied during projection.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "ve_effective_viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 997, - "signature": "(inner_self)", + "name": "uw_weighting_function", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2896, + "signature": "(self, user_uw_function)", "parameters": [ { - "name": "inner_self", + "name": "user_uw_function", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "the dt_elastic defaults to infinity, t_relax to zero,", - "so this should be well behaved in the viscous limit", - "# The effective viscosity depends on the number of history terms", - "## creates list of values that has the same length as the material index", - "# The effective viscosity depends on the number of history terms" - ], - "status": "none", + "existing_docstring": "Set the weighting function for the projection.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Projection", "is_public": true }, { - "name": "plastic_eff_viscosity", + "name": "smoothing", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1091, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 3011, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "+ inner_self.strainrate_inv_II_min", - "## creates list of values that has the same length as the material index" - ], - "status": "none", + "existing_docstring": "Smoothing coefficient :math:`\\alpha` (units **length\u00b2**).\n\nCoefficient of the :math:`\\nabla\\!\\cdot\\!(\\alpha\\,\\nabla\n\\mathbf u)` term in the vector screened-Poisson equation\n(see the class docstring). Acts component-wise; the\nsmoothing length is :math:`L = \\sqrt{\\alpha}` and the\nGreen's function decays as :math:`\\exp(-r/L)`.\n\nUse :attr:`smoothing_length` for the L-valued, unit-aware\nknob. See :attr:`SNES_Projection.smoothing` for the full\nderivation and usage patterns.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1180, - "signature": "(inner_self)", + "name": "smoothing", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3027, + "signature": "(self, smoothing_factor)", "parameters": [ { - "name": "inner_self", + "name": "smoothing_factor", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "detect if values we need are defined or are placeholder symbols" - ], - "status": "none", + "existing_docstring": "Set the smoothing regularization parameter.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "is_elastic", + "name": "smoothing_length", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1440, + "file": "src/underworld3/systems/solvers.py", + "line": 3033, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Smoothing length :math:`L` (length units, **unit-aware**).\n\nL-valued view on :attr:`smoothing`, with\n:math:`L = \\sqrt{\\alpha}`. The projection then acts as a\ncomponent-wise screened-Poisson smoother\n:math:`\\mathbf u - L^{2}\\,\\nabla^{2}\\mathbf u =\n\\tilde{\\mathbf f}`, i.e. a Gaussian-like convolution of\nwidth :math:`L` applied to each Cartesian component of\nthe input vector field.\n\nChoose :math:`L \\gtrsim h` (a cell size) for noticeable\nsmoothing; :math:`L < h` does essentially nothing because\nthe discretisation already band-limits at that scale.\nSee :attr:`SNES_Projection.smoothing_length` for the full\nmathematical and units discussion.", "harvested_comments": [ - "If any of these is not defined, elasticity is switched off" + "Return a Pint Quantity only if the user set a dimensional value via", + "the setter; plain-float input round-trips as a plain float." ], - "status": "none", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SNES_Vector_Projection", + "is_public": true + }, + { + "name": "smoothing_length", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3068, + "signature": "(self, L)", + "parameters": [ + { + "name": "L", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the smoothing length scale (unit-aware).", + "harvested_comments": [ + "Unit-aware: a dimensional input (Pint Quantity / UnitAware) is", + "non-dimensionalised through the active scaling context and reduced to", + "a plain float (uw.non_dimensionalise returns a dimensionless", + "UWQuantity, which sympify can't square); a plain number is taken as", + "already non-dimensional." + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "is_viscoplastic", + "name": "penalty", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1455, + "file": "src/underworld3/systems/solvers.py", + "line": 3088, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Divergence penalty :math:`\\lambda` for (approx.) incompressibility.\n\nCoefficient of the :math:`\\lambda (\\nabla\\!\\cdot\\!\\mathbf u)\n\\mathbf I` term in the vector projection. Large positive\nvalues bias the projection toward\n:math:`\\nabla\\!\\cdot\\!\\mathbf u = 0`, i.e. a solenoidal\napproximation of :math:`\\tilde{\\mathbf f}`. Has no length\ninterpretation \u2014 unlike :attr:`smoothing` it does not\nintroduce a filter scale.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "diffusivity", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1502, - "signature": "(inner_self)", + "name": "penalty", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3102, + "signature": "(self, value)", "parameters": [ { - "name": "inner_self", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the divergence penalty parameter.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "diffusivity", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1506, - "signature": "(inner_self, value: Union[float, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[float, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "uw_weighting_function", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3109, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Weighting function applied during projection.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "eta_0", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1594, - "signature": "(inner_self)", + "name": "uw_weighting_function", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3114, + "signature": "(self, user_uw_function)", "parameters": [ { - "name": "inner_self", + "name": "user_uw_function", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the weighting function for the projection.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Vector_Projection", "is_public": true }, { - "name": "eta_0", + "name": "solve", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1598, - "signature": "(inner_self, value: Union[float, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 3201, + "signature": "(self, verbose = False, divergence_retries: int = 0)", "parameters": [ { - "name": "inner_self", + "name": "verbose", "type_hint": null, - "default": null, + "default": "False", "description": "" }, { - "name": "value", - "type_hint": "Union[float, sympy.Function]", - "default": null, + "name": "divergence_retries", + "type_hint": "int", + "default": "0", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Solve by projecting each tensor component sequentially.\n\nParameters\n----------\nverbose : bool\ndivergence_retries : int, default=0\n Forwarded to each per-component SNES solve. 0 preserves\n legacy behaviour.", + "harvested_comments": [ + "Loop over the components of the tensor. If this is a symmetric", + "tensor, we'll usually be given the 1d form to prevent duplication", + "if self.t_field.sym_1d.shape != self.uw_function.shape:", + "raise ValueError(", + "\"Tensor shapes for uw_function and MeshVariable are not the same\"" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Tensor_Projection", "is_public": true }, { - "name": "eta_1", + "name": "F0", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1606, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 3241, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise misfit term for scalar subproblem.", + "harvested_comments": [ + "backward compatibility" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Tensor_Projection", "is_public": true }, { - "name": "eta_1", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1610, - "signature": "(inner_self, value: Union[float, sympy.Function])", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "Union[float, sympy.Function]", - "default": null, - "description": "" - } - ], + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3255, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise smoothing flux term for scalar subproblem.", + "harvested_comments": [ + "backward compatibility" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Tensor_Projection", "is_public": true }, { - "name": "director", + "name": "uw_scalar_function", "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1618, - "signature": "(inner_self)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/systems/solvers.py", + "line": 3269, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Current scalar component function being projected.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Tensor_Projection", "is_public": true }, { - "name": "director", + "name": "uw_scalar_function", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1622, - "signature": "(inner_self, value: Union[sympy.Matrix, sympy.Function])", + "file": "src/underworld3/systems/solvers.py", + "line": 3274, + "signature": "(self, user_uw_function)", "parameters": [ { - "name": "inner_self", + "name": "user_uw_function", "type_hint": null, "default": null, "description": "" - }, - { - "name": "value", - "type_hint": "Union[sympy.Matrix, sympy.Function]", - "default": null, - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the scalar component function for current tensor element.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "SNES_Tensor_Projection", "is_public": true }, { - "name": "petsc_fvm_get_min_radius", - "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 14, - "signature": "def petsc_fvm_get_min_radius(mesh) -> float:", + "name": "smoothing", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3370, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Smoothing coefficient :math:`\\alpha` (units **length\u00b2**).\n\nCoefficient of the :math:`\\nabla\\!\\cdot\\!(\\alpha\\,\\nabla\nu_k)` term in each component's screened-Poisson sub-problem\n(see the class docstring). One :math:`\\alpha` is shared\nacross all :math:`N_c` components, so the implied smoothing\nlength :math:`L = \\sqrt{\\alpha}` is uniform.\n\nUse :attr:`smoothing_length` for the L-valued, unit-aware\nknob. See :attr:`SNES_Projection.smoothing` for the full\nderivation and the Gaussian-like convolution interpretation.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "SNES_MultiComponent_Projection", "is_public": true }, { - "name": "petsc_fvm_get_local_cell_sizes", - "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 35, - "signature": "def petsc_fvm_get_local_cell_sizes(mesh) -> np.array:", + "name": "smoothing_length", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3391, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Smoothing length :math:`L` (length units, **unit-aware**).\n\nL-valued view on :attr:`smoothing`, with\n:math:`L = \\sqrt{\\alpha}`. Each component then satisfies\n:math:`u_k - L^{2}\\,\\nabla^{2} u_k = \\tilde f_k`, i.e. a\nGaussian-like convolution of width :math:`L` applied\nindependently to each component of the multi-component\ntarget. See :attr:`SNES_Projection.smoothing_length` for the\nfull mathematical and units discussion.", + "harvested_comments": [ + "Return a Pint Quantity only if the user set a dimensional value via", + "the setter; plain-float input round-trips as a plain float." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "SNES_MultiComponent_Projection", "is_public": true }, { - "name": "petsc_dm_create_submesh_from_label", + "name": "smoothing_length", "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 61, - "signature": "def petsc_dm_create_submesh_from_label(incoming_dm, boundary_label_name, boundary_label_value, marked_faces=True) -> float:", - "parameters": [], + "file": "src/underworld3/systems/solvers.py", + "line": 3420, + "signature": "(self, L)", + "parameters": [ + { + "name": "L", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Set the smoothing length scale (unit-aware).", + "harvested_comments": [ + "Unit-aware: a dimensional input (Pint Quantity / UnitAware) is", + "non-dimensionalised through the active scaling context and reduced to", + "a plain float (uw.non_dimensionalise returns a dimensionless", + "UWQuantity, which sympify can't square); a plain number is taken as", + "already non-dimensional." + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MultiComponent_Projection", "is_public": true }, { - "name": "petsc_dm_find_labeled_points_local", - "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 85, - "signature": "def petsc_dm_find_labeled_points_local(dm, label_name, sectionIndex=False, verbose=False):", + "name": "uw_weighting_function", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3440, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Weighting function applied during projection.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_MultiComponent_Projection", "is_public": true }, { - "name": "petsc_dm_get_periodicity", - "kind": "function", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 150, - "signature": "def petsc_dm_get_periodicity(incoming_dm):", + "name": "F0", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3731, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise source term including time derivative.", + "harvested_comments": [ + "backward compatibility" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "petsc_dm_set_periodicity", - "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 184, - "signature": "def petsc_dm_set_periodicity(incoming_dm, maxCell, Lstart, L):", + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3745, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise diffusive flux term (Adams-Moulton integration).", + "harvested_comments": [ + "backward compatibility" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "petsc_vec_concatenate", + "name": "adv_diff_slcn_problem_description", "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 221, - "signature": "def petsc_vec_concatenate( inputVecs ):", + "file": "src/underworld3/systems/solvers.py", + "line": 3758, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Build residual terms for advection-diffusion FEM assembly.", + "harvested_comments": [ + "f0 residual term", + "f1 residual term" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "petsc_get_swarm_coord_name", - "kind": "method", - "file": "src/underworld3/cython/petsc_discretisation.pyx", - "line": 244, - "signature": "def petsc_get_swarm_coord_name( sdm ):", + "name": "f", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3769, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Source term for the advection-diffusion equation.\n\nThe source :math:`f` appears on the right-hand side:\n\n.. math::\n \\frac{\\partial u}{\\partial t} + \\mathbf{V} \\cdot \\nabla u\n = \\nabla \\cdot (\\kappa \\nabla u) + f\n\nReturns\n-------\nsympy.Matrix\n Source term expression.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "has_meaningful_units", + "name": "f", "kind": "method", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 131, - "signature": "def has_meaningful_units(u):", - "parameters": [], + "file": "src/underworld3/systems/solvers.py", + "line": 3786, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the volumetric source term.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "evaluate", - "kind": "method", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 247, - "signature": "def evaluate(self) -> float:", + "name": "V_fn", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3792, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Velocity field for advection.\n\nThe advection velocity :math:`\\mathbf{V}` transports the scalar\nfield :math:`u`. Can be a MeshVariable or symbolic expression.\n\nReturns\n-------\nsympy.Matrix\n Velocity vector expression.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "extend_enum", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 34, - "signature": "(inherited)", + "name": "V_fn", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3806, + "signature": "(self, value)", "parameters": [ { - "name": "inherited", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the velocity function for advection.\n\nParameters:\n-----------\nvalue : uw.discretisation.MeshVariable or sympy.Basic\n Velocity field as either a MeshVariable or sympy expression", + "harvested_comments": [ + "Mark as needing setup when velocity changes" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SNES_AdvectionDiffusion", + "is_public": true + }, + { + "name": "delta_t", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 3822, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Timestep for time integration.\n\nThe timestep :math:`\\Delta t` controls the temporal discretization.\nFor explicit advection, this should satisfy the CFL condition:\n\n.. math::\n \\Delta t < \\frac{h}{|\\mathbf{V}|}\n\nwhere :math:`h` is the element size and :math:`|\\mathbf{V}|` is the\nvelocity magnitude.\n\nReturns\n-------\nUWexpression\n Timestep value.\n\nSee Also\n--------\nestimate_dt : Computes a stable timestep automatically.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "nuke_coords_and_rebuild", + "name": "delta_t", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1087, - "signature": "(self, verbose = False)", + "file": "src/underworld3/systems/solvers.py", + "line": 3846, + "signature": "(self, value)", "parameters": [ { - "name": "verbose", + "name": "value", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the timestep (handles unit conversion if provided).", "harvested_comments": [ - "This is a reversion to the old version (3.15 compatible which seems to work in 3.16 too)", - "let's go ahead and do an initial projection from linear (the default)", - "to linear. this really is a nothing operation, but a", - "side effect of this operation is that coordinate DM DMField is", - "converted to the required `PetscFE` type. this may become necessary" + "Note: comparison must handle potential UWexpressions / UWQuantities", + "Use .data or float() to get numeric values for stable comparison", + "Handle Pint Quantities with time dimensions", + "This is a Pint Quantity - check if it has time dimensions", + "Convert physical time to nondimensional using model time scale" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "checkpoint_xdmf", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 3124, - "signature": "(filename: str, meshUpdates: bool = True, meshVars: Optional[list] = [], swarmVars: Optional[list] = [], index: Optional[int] = 0)", + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3901, + "signature": "(self, direction_aware: bool = False, percentile: float = 0.0)", "parameters": [ { - "name": "filename", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "meshUpdates", + "name": "direction_aware", "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "meshVars", - "type_hint": "Optional[list]", - "default": "[]", - "description": "" - }, - { - "name": "swarmVars", - "type_hint": "Optional[list]", - "default": "[]", + "default": "False", "description": "" }, { - "name": "index", - "type_hint": "Optional[int]", - "default": "0", + "name": "percentile", + "type_hint": "float", + "default": "0.0", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Estimate an appropriate timestep for the advection-diffusion solver.\n\nThis is an implicit solver so the returned :math:`\\delta t` is the\nminimum of:\n\n- :math:`\\delta t_{\\textrm{diff}}`: typical time for diffusion across an element\n- :math:`\\delta t_{\\textrm{adv}}`: typical element-crossing time for a fluid parcel\n\nParameters\n----------\ndirection_aware : bool, default False\n If True, the advective dt uses the per-cell extent\n *along the local velocity direction* \u2014 `h_eff_c =\n max_i(s_i) - min_i(s_i)` where `s_i = (x_i -\n centroid) \u00b7 v\u0302` over the cell vertices. This is the\n distance material actually traverses through the cell\n per unit ``|v|``, and is **always \u2265 the isotropic\n mesh._radii estimate**, by 1.5\u20133\u00d7 for equant cells\n (geometric factor) and up to ~10\u00d7 for cells that the\n mover has stretched along the flow direction. On\n adapted meshes the gain is substantial; on uniform\n meshes it's the geometric factor only. Off by\n default to preserve historical behaviour; safe to\n enable everywhere once validated.\n\nReturns\n-------\npint.Quantity or float\n The recommended timestep with physical time units if a model\n with reference scales is available, otherwise nondimensional.", "harvested_comments": [ - "# Identify the mesh file. Use the", - "# zeroth one if this option is turned off", - "# Obtain the mesh information", - "We only use a subset of the possible cell types", - "# Create the header" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "## required modules", + "Use the unified .K property from the constitutive model", + "This provides diffusivity for diffusion models", + "Evaluate the diffusivity (handles constant and spatially-varying cases)", + "If diffusivity is unit-aware (UnitAwareArray), nondimensionalise it to get" ], - "parent_class": null, + "status": "complete", + "needs": [], + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "extend_enum", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 61, - "signature": "(inherited)", + "name": "solve", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4093, + "signature": "(self, zero_init_guess: bool = True, timestep: float = None, _force_setup: bool = False, _evalf = False, verbose = False, divergence_retries: int = 0)", "parameters": [ { - "name": "inherited", + "name": "zero_init_guess", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "timestep", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "_force_setup", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "_evalf", "type_hint": null, - "default": null, + "default": "False", "description": "" - } - ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "load_from_h5_plex_vector", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1217, - "signature": "(self, filename: str, data_name: Optional[str] = None)", - "parameters": [ + }, { - "name": "filename", - "type_hint": "str", - "default": null, + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "data_name", - "type_hint": "Optional[str]", - "default": "None", + "name": "divergence_retries", + "type_hint": "int", + "default": "0", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.\ndivergence_retries:\n If SNES reports DIVERGED, retry with warm start up to this\n many times. 0 preserves legacy behaviour.", "harvested_comments": [ - "Ensure vectors are initialized" + "this will force an initialisation because the functions need to be updated", + "Update History / Flux History terms", + "SemiLagrange and Lagrange may have different sequencing.", + "Invalidate cached data views - PETSc may have replaced underlying buffers", + "This ensures .data and .array properties return fresh data from PETSc" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SNES_AdvectionDiffusion", "is_public": true }, { - "name": "create_structure", - "kind": "method", - "file": "src/underworld3/function/_dminterp_wrapper.pyx", - "line": 78, - "signature": "def create_structure(self, mesh, np.ndarray[double, ndim=2] coords,\n np.ndarray[long, ndim=1] cells, int dofcount):", + "name": "F0", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4308, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise source term including time derivative.", + "harvested_comments": [ + "backward compatibility" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "evaluate", - "kind": "method", - "file": "src/underworld3/function/_dminterp_wrapper.pyx", - "line": 144, - "signature": "def evaluate(self, mesh, np.ndarray[double, ndim=2] outarray):", + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4322, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise diffusive flux term.", + "harvested_comments": [ + "backward compatibility" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "fdiff", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 90, - "signature": "def fdiff(self,argindex):", + "name": "f", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4336, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Source term for the diffusion equation.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "global_evaluate_nd", + "name": "f", "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 288, - "signature": "def global_evaluate_nd( expr,\n coords=None,\n coord_sys=None,\n other_arguments=None,\n simplify=True,\n verbose=False,\n evalf=False,\n rbf=False,\n data_layout=None,\n check_extrapolated=False,\n force_l2=False,\n smoothing=1e-6,\n ):", - "parameters": [], + "file": "src/underworld3/systems/solvers.py", + "line": 4341, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the volumetric source term.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "evaluate_nd", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 662, - "signature": "def evaluate_nd( expr,\n coords=None,\n coord_sys=None,\n other_arguments=None,\n simplify=True,\n verbose=False,\n evalf=False,\n rbf=False,\n data_layout=None,\n check_extrapolated=False,\n force_l2=False,\n smoothing=1e-6):", + "name": "delta_t", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4347, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Timestep for time integration.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "petsc_interpolate", + "name": "delta_t", "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 838, - "signature": "def petsc_interpolate( expr,\n np.ndarray coords=None,\n coord_sys=None,\n mesh=None,\n other_arguments=None,\n simplify=True,\n verbose=False, ):", - "parameters": [], + "file": "src/underworld3/systems/solvers.py", + "line": 4352, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Set the timestep (handles unit conversion if provided).", + "harvested_comments": [ + "Note: comparison must handle potential UWexpressions / UWQuantities", + "Use .data or float() to get numeric values for stable comparison", + "Handle Pint Quantities with time dimensions", + "This is a Pint Quantity - check if it has time dimensions", + "Convert physical time to nondimensional using model time scale" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "interpolate_vars_on_mesh", + "name": "estimate_dt", "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 977, - "signature": "def interpolate_vars_on_mesh( varfns, np.ndarray coords ):", + "file": "src/underworld3/systems/solvers.py", + "line": 4407, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Estimate an appropriate timestep for the diffusion solver.\n\nThis solver only has a diffusive component, so the returned\n:math:`\\delta t` is:\n\n- :math:`\\delta t_{\\textrm{diff}}`: typical time for diffusion across an element\n\nReturns\n-------\npint.Quantity or float\n The diffusive timestep with physical time units if a model\n with reference scales is available, otherwise nondimensional.", + "harvested_comments": [ + "## required modules", + "Use the unified .K property from the constitutive model", + "This provides diffusivity for diffusion models", + "Evaluate the diffusivity (handles constant and spatially-varying cases)", + "If diffusivity is unit-aware (UnitAwareArray), nondimensionalise it to get" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "rbf_evaluate", + "name": "solve", "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 1119, - "signature": "def rbf_evaluate( expr,\n coords=None,\n coord_sys=None,\n mesh=None,\n other_arguments=None,\n verbose=False,\n simplify=True,):", - "parameters": [], + "file": "src/underworld3/systems/solvers.py", + "line": 4482, + "signature": "(self, zero_init_guess: bool = True, timestep: float = None, evalf: bool = False, _force_setup: bool = False, verbose = False, divergence_retries: int = 0)", + "parameters": [ + { + "name": "zero_init_guess", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "timestep", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "evalf", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "_force_setup", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "divergence_retries", + "type_hint": "int", + "default": "0", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.\ndivergence_retries:\n If SNES reports DIVERGED, retry with warm start up to this\n many times. 0 preserves legacy behaviour.", + "harvested_comments": [ + "this will force an initialisation because the functions need to be updated", + "self._flux = self.constitutive_model.flux.T", + "self._flux_star = self._flux.copy()", + "Update History / Flux History terms", + "SemiLagrange and Lagrange may have different sequencing." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "SNES_Diffusion", "is_public": true }, { - "name": "dm_swarm_get_migrate_type", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 1241, - "signature": "def dm_swarm_get_migrate_type(swarm):", + "name": "F0", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4713, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Pointwise momentum source term (body force + inertia).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "dm_swarm_set_migrate_type", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 1253, - "signature": "def dm_swarm_set_migrate_type(swarm, mtype:PETsc.DMSwarm.MigrateType):", + "name": "F1", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4728, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Pointwise stress flux term (viscous + pressure).", + "harvested_comments": [ + "We can flag to only do this if the constitutive model has been updated", + "Is the else condition useful - other than to prevent a crash ?", + "Yes, because then it can just live on the Stokes solver ..." + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "eval", - "kind": "method", - "file": "src/underworld3/function/analytic.pyx", - "line": 58, - "signature": "def eval(cls, *args ):", + "name": "PF0", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4759, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Pointwise constraint term (continuity equation).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "eval", + "name": "navier_stokes_problem_description", "kind": "method", - "file": "src/underworld3/function/analytic.pyx", - "line": 76, - "signature": "def eval(cls, *args ):", + "file": "src/underworld3/systems/solvers.py", + "line": 4774, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Build residual terms for Navier-Stokes FEM assembly (deprecated).", + "harvested_comments": [ + "f0 residual term", + "f1 residual term", + "p1 residual term" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "description", + "name": "delta_t", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 907, + "file": "src/underworld3/systems/solvers.py", + "line": 4788, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Timestep for time integration.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "description", + "name": "delta_t", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 911, + "file": "src/underworld3/systems/solvers.py", + "line": 4793, "signature": "(self, value)", "parameters": [ { @@ -7765,206 +7310,253 @@ } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the timestep value.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "rbf_interpolator_local", + "name": "rho", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4799, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Fluid density.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "rho", "kind": "method", - "file": "src/underworld3/kdtree.py", - "line": 128, - "signature": "(self, coords, data, nnn = 4, p = 2, verbose = False)", + "file": "src/underworld3/systems/solvers.py", + "line": 4804, + "signature": "(self, value)", "parameters": [ { - "name": "coords", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "data", + "name": "value", "type_hint": null, "default": null, "description": "" - }, - { - "name": "nnn", - "type_hint": null, - "default": "4", - "description": "" - }, - { - "name": "p", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the fluid density.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "KDTree", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "write_proxy", + "name": "f", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4810, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Source term for the momentum equation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "f", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1827, - "signature": "(self, filename: str)", + "file": "src/underworld3/systems/solvers.py", + "line": 4815, + "signature": "(self, value)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "value", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "if not proxied, nothing to do. return." + "existing_docstring": "Set the volumetric source term.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "none", + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "div_u", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4821, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Velocity divergence: :math:`\\nabla \\cdot \\mathbf{u} = \\mathrm{tr}(\\dot{\\varepsilon})`.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "read_timestep", + "name": "strainrate", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4828, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Symmetric strain rate tensor (with 1/2 factor).\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "DuDt", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4838, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Time derivative operator for velocity.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "DuDt", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1838, - "signature": "(self, data_filename: str, swarmID: str, data_name: str, index: int, outputPath = '')", + "file": "src/underworld3/systems/solvers.py", + "line": 4843, + "signature": "(self, DuDt_value: Union[SemiLagrangian_DDt, Lagrangian_DDt])", "parameters": [ { - "name": "data_filename", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "swarmID", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "data_name", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "index", - "type_hint": "int", + "name": "DuDt_value", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", "default": null, "description": "" - }, - { - "name": "outputPath", - "type_hint": null, - "default": "''", - "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "mesh.write_timestep( \"test\", meshUpdates=False, meshVars=[X], outputPath=\"\", index=0)", - "swarm.write_timestep(\"test\", \"swarm\", swarmVars=[var], outputPath=\"\", index=0)", - "check if swarmFilename exists", - "easier to debug abs path", - "## open up file with coords on all procs and open up data on all procs. May be problematic for large problems." - ], - "status": "none", + "existing_docstring": "Set the time derivative operator for velocity.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "sym_1d", + "name": "DFDt", "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2043, + "file": "src/underworld3/systems/solvers.py", + "line": 4852, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Time derivative operator for stress flux.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "visMask", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2085, + "name": "constraints", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4857, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Constraint equation (typically incompressibility).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "clip_to_mesh", + "name": "constraints", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4862, + "signature": "(self, constraints_matrix)", + "parameters": [ + { + "name": "constraints_matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the constraint equation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "bodyforce", "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2818, + "file": "src/underworld3/systems/solvers.py", + "line": 4869, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Body force vector (e.g., gravity).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "clip_to_mesh", + "name": "bodyforce", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2822, + "file": "src/underworld3/systems/solvers.py", + "line": 4874, "signature": "(self, value)", "parameters": [ { @@ -7975,738 +7567,724 @@ } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Set the body force vector.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "read_timestep", + "name": "saddle_preconditioner", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4880, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Preconditioner for the Schur complement.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "saddle_preconditioner", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3474, - "signature": "(self, base_filename: str, swarm_id: str, index: int, outputPath: Optional[str] = '', migrate = True)", + "file": "src/underworld3/systems/solvers.py", + "line": 4885, + "signature": "(self, value)", "parameters": [ { - "name": "base_filename", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "swarm_id", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "index", - "type_hint": "int", - "default": null, - "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" - }, - { - "name": "migrate", + "name": "value", "type_hint": null, - "default": "True", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "## open up file with coords on all procs", - "We make it possible not to migrate the swarm because this", - "will also delete points outside the mesh. We may not want to do", - "that (either for debugging / visualisation, or when adapting the mesh)" + "existing_docstring": "Set the Schur complement preconditioner.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "none", + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "penalty", + "kind": "property", + "file": "src/underworld3/systems/solvers.py", + "line": 4892, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Augmented Lagrangian penalty parameter.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "SNES_NavierStokes", "is_public": true }, { - "name": "advection", + "name": "penalty", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 4002, - "signature": "(self, V_fn, delta_t, order = 2, corrector = False, restore_points_to_domain_func = None, evalf = False, step_limit = False)", + "file": "src/underworld3/systems/solvers.py", + "line": 4897, + "signature": "(self, value)", "parameters": [ { - "name": "V_fn", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "delta_t", + "name": "value", "type_hint": null, "default": null, "description": "" - }, - { - "name": "order", - "type_hint": null, - "default": "2", - "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Set the augmented Lagrangian penalty parameter.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "solve", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4904, + "signature": "(self, zero_init_guess: bool = True, timestep: float = None, _force_setup: bool = False, verbose = False, _evalf = False, order = None, divergence_retries: int = 0)", + "parameters": [ { - "name": "corrector", - "type_hint": null, - "default": "False", + "name": "zero_init_guess", + "type_hint": "bool", + "default": "True", "description": "" }, { - "name": "restore_points_to_domain_func", - "type_hint": null, + "name": "timestep", + "type_hint": "float", "default": "None", "description": "" }, { - "name": "evalf", + "name": "_force_setup", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "verbose", "type_hint": null, "default": "False", "description": "" }, { - "name": "step_limit", + "name": "_evalf", "type_hint": null, "default": "False", "description": "" + }, + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "divergence_retries", + "type_hint": "int", + "default": "0", + "description": "" } ], "returns": null, + "existing_docstring": "Generates solution to constructed system.\n\nParams\n------\nzero_init_guess:\n If `True`, a zero initial guess will be used for the\n system solution. Otherwise, the current values of `self.u` will be used.\ndivergence_retries:\n If SNES reports DIVERGED, retry with warm start up to this\n many times. 0 preserves legacy behaviour.", + "harvested_comments": [ + "this will force an initialisation because the functions need to be updated", + "Update SemiLagrange Flux terms", + "Override AM coefficients if flux_order is explicitly set" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4981, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Estimate an appropriate timestep for the Navier-Stokes solver.\n\nThis is an implicit solver, so the returned :math:`\\delta t` should\nbe interpreted as:\n\n- :math:`\\delta t_{\\textrm{diff}}`: typical time for vorticity diffusion across an element\n- :math:`\\delta t_{\\textrm{adv}}`: typical element-crossing time for a fluid parcel\n\nThe Navier-Stokes equations include momentum diffusion via kinematic\nviscosity :math:`\\nu = \\eta/\\rho`, so the diffusive timestep is computed\nfrom this quantity.\n\nReturns\n-------\ntuple\n (:math:`\\delta t_{\\textrm{diff}}`, :math:`\\delta t_{\\textrm{adv}}`)", + "harvested_comments": [ + "## required modules", + "For Navier-Stokes, diffusivity is the kinematic viscosity: \u03bd = \u03b7/\u03c1", + "Use the unified .K property from the constitutive model (returns viscosity)", + "Evaluate the viscosity (handles constant and spatially-varying cases)", + "If diffusivity is unit-aware (UnitAwareArray), nondimensionalise it to get" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": true + }, + { + "name": "view", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 166, + "signature": "()", + "parameters": [], + "returns": null, "existing_docstring": null, "harvested_comments": [ - "Convert delta_t to model units if it has units", - "This ensures consistent arithmetic: velocity is in model units, so time must be too", - "X0 holds the particle location at the start of advection", - "This is needed because the particles may be migrated off-proc", - "during timestepping. Probably not needed - use global evaluation instead" + "# Docstring (static)", + "docstring = docstring.replace(\"$\", \"$\").replace(\"$\", \"$\")" ], "status": "none", "needs": [ "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": null, "is_public": true }, { - "name": "advection", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 4397, - "signature": "(self, V_fn, delta_t, order = 2, corrector = False, restore_points_to_domain_func = None, evalf = False, step_limit = True)", + "name": "mesh_adapt_meshVar", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 588, + "signature": "(mesh, meshVarH, metricVar, verbose = False, redistribute = False)", "parameters": [ { - "name": "V_fn", + "name": "mesh", "type_hint": null, "default": null, "description": "" }, { - "name": "delta_t", + "name": "meshVarH", "type_hint": null, "default": null, "description": "" }, { - "name": "order", + "name": "metricVar", "type_hint": null, - "default": "2", + "default": null, "description": "" }, { - "name": "corrector", + "name": "verbose", "type_hint": null, "default": "False", "description": "" }, { - "name": "restore_points_to_domain_func", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "evalf", + "name": "redistribute", "type_hint": null, "default": "False", "description": "" - }, - { - "name": "step_limit", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, "existing_docstring": null, - "harvested_comments": [], + "harvested_comments": [ + "Create / use a field on the old mesh to hold the metric", + "Perhaps that should be a user-definition" + ], "status": "none", "needs": [ "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "NodalPointSwarm", + "parent_class": null, "is_public": true }, { - "name": "use_nondimensional_scaling", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 268, - "signature": "(enabled = True)", + "name": "save_vector", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 30, + "signature": "(self, key: str, array: np.ndarray) -> None", "parameters": [ { - "name": "enabled", - "type_hint": null, - "default": "True", + "name": "key", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "array", + "type_hint": "np.ndarray", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Enable or disable non-dimensional scaling globally.\n\nWhen enabled, equations are scaled during unwrap() for better numerical\nconditioning. Variables with units and reference scales are automatically\nscaled (divided by their scaling_coefficient).\n\nThis is the ONLY way to control non-dimensional scaling - there are no\nother flags, solver settings, or context managers that affect this.\n\nParameters\n----------\nenabled : bool, default=True\n True to enable non-dimensional scaling, False to disable\n\nNotes\n-----\n- Scaling coefficients are ALWAYS computed from model.set_reference_quantities()\n- This flag only controls whether those coefficients are APPLIED during unwrap()\n- Changing this flag requires recompiling solvers (set solver.is_setup=False)\n\nExamples\n--------\nSetup problem with reference quantities:\n\n>>> model = uw.Model()\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(100, \"km\"),\n... temperature_diff=uw.quantity(1000, \"kelvin\")\n... )\n>>> T = uw.discretisation.MeshVariable('T', mesh, 1, units='kelvin')\n\nSolve with dimensional form (default):\n\n>>> uw.use_nondimensional_scaling(False) # Default\n>>> poisson.solve()\n>>> u_dimensional = T.array.copy()\n\nSolve with non-dimensional form (for comparison):\n\n>>> uw.use_nondimensional_scaling(True)\n>>> poisson.is_setup = False # Force recompilation with new scaling\n>>> poisson.solve()\n>>> u_nondimensional = T.array.copy()\n>>>\n>>> # Solutions should be identical\n>>> import numpy as np\n>>> assert np.allclose(u_dimensional, u_nondimensional)\n\nSee Also\n--------\nis_nondimensional_scaling_active : Check current scaling state\nmodel.set_reference_quantities : Set reference scales", - "harvested_comments": [ - "Force recompilation with new scaling", - "Solutions should be identical" - ], - "status": "partial", + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CheckpointBackend", "is_public": true }, { - "name": "is_nondimensional_scaling_active", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 327, - "signature": "()", - "parameters": [], - "returns": null, - "existing_docstring": "Check if non-dimensional scaling is currently enabled.\n\nReturns\n-------\nbool\n True if non-dimensional scaling is active, False otherwise\n\nSee Also\n--------\nuse_nondimensional_scaling : Enable/disable scaling", + "name": "load_vector", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 32, + "signature": "(self, key: str) -> np.ndarray", + "parameters": [ + { + "name": "key", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "np.ndarray", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CheckpointBackend", "is_public": true }, { - "name": "nondimensional_scaling_context", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 344, - "signature": "(enabled = True)", + "name": "save_metadata", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 34, + "signature": "(self, key: str, value: Any) -> None", "parameters": [ { - "name": "enabled", - "type_hint": null, - "default": "True", + "name": "key", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": "Any", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Context manager to temporarily set non-dimensional scaling state.\n\nAutomatically restores the previous state when exiting the context.\nThis is useful for operations that need to temporarily work in\nnon-dimensional space (like semi-Lagrangian advection) without\naffecting the global state.\n\nParameters\n----------\nenabled : bool, default=True\n True to enable non-dimensional scaling within context\n\nExamples\n--------\n>>> # Temporarily enable non-dimensional mode\n>>> with uw.nondimensional_scaling_context(True):\n... # evaluate() returns non-dimensional results here\n... velocity_nd = uw.function.evaluate(u.sym, coords)\n>>> # Original state restored automatically\n\nSee Also\n--------\nuse_nondimensional_scaling : Permanently set scaling state\nis_nondimensional_scaling_active : Check current state", - "harvested_comments": [ - "Temporarily enable non-dimensional mode", - "evaluate() returns non-dimensional results here", - "Original state restored automatically" + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "CheckpointBackend", "is_public": true }, { - "name": "use_strict_units", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 392, - "signature": "(enabled = True)", + "name": "load_metadata", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 36, + "signature": "(self, key: str) -> Any", "parameters": [ { - "name": "enabled", - "type_hint": null, - "default": "True", + "name": "key", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Enable or disable strict units enforcement.\n\n**DEFAULT: ON** - Strict units mode is enabled by default to enforce best\npractices from the start. Variables with units REQUIRE reference quantities.\n\nWhen disabled, variables with units are allowed without reference quantities\nbut will get scaling_coefficient=1.0 and a warning. This \"half-way zone\"\nleads to poor numerical conditioning and silent errors.\n\n**Disabling strict mode is only recommended for:**\n- Expert users who understand the implications\n- Debugging specific issues\n- Legacy code migration (temporary)\n\nParameters\n----------\nenabled : bool, default=True\n True to enforce strict units (DEFAULT - recommended)\n False to allow units without reference quantities (expert/debugging only)\n\nExamples\n--------\nNormal usage (strict mode ON by default):\n\n>>> import underworld3 as uw\n>>> # Strict mode is ON by default - no need to enable\n>>>\n>>> # Set reference quantities FIRST:\n>>> model = uw.get_default_model()\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(1000, 'km'),\n... plate_velocity=uw.quantity(5, 'cm/year')\n... )\n>>>\n>>> # Then create mesh and variables:\n>>> mesh = uw.meshing.StructuredQuadBox(elementRes=(4, 4))\n>>> v = uw.discretisation.MeshVariable(\"v\", mesh, 2, units=\"m/s\") # \u2713 OK\n\nDisable for debugging (expert use only):\n\n>>> uw.use_strict_units(False) # Expert/debugging only\n>>> mesh = uw.meshing.StructuredQuadBox(elementRes=(4, 4))\n>>> v = uw.discretisation.MeshVariable(\"v\", mesh, 2, units=\"m/s\") # \u26a0\ufe0f Warning\n\nSee Also\n--------\nis_strict_units_active : Check current strict mode\nModel.set_reference_quantities : Set reference quantities", - "harvested_comments": [ - "Strict mode is ON by default - no need to enable", - "Set reference quantities FIRST:", - "Then create mesh and variables:", - "Expert/debugging only" + "returns": "Any", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "partial", + "parent_class": "CheckpointBackend", + "is_public": true + }, + { + "name": "list_vectors", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 38, + "signature": "(self) -> list[str]", + "parameters": [], + "returns": "list[str]", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CheckpointBackend", "is_public": true }, { - "name": "is_strict_units_active", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 447, - "signature": "()", + "name": "list_metadata", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 40, + "signature": "(self) -> list[str]", "parameters": [], - "returns": null, - "existing_docstring": "Check if strict units enforcement is enabled.\n\nReturns\n-------\nbool\n True if strict units mode is active, False otherwise\n\nSee Also\n--------\nuse_strict_units : Enable/disable strict units", + "returns": "list[str]", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CheckpointBackend", "is_public": true }, { - "name": "unwrap", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 493, - "signature": "(fn, keep_constants = True, return_self = True, apply_scaling = False)", + "name": "save_vector", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 56, + "signature": "(self, key: str, array: np.ndarray) -> None", "parameters": [ { - "name": "fn", - "type_hint": null, + "name": "key", + "type_hint": "str", "default": null, "description": "" }, { - "name": "keep_constants", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "return_self", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "apply_scaling", - "type_hint": null, - "default": "False", + "name": "array", + "type_hint": "np.ndarray", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Unwrap expressions with optional automatic scaling.\n\nParameters:\n-----------\nfn : expression\n The expression to unwrap\nkeep_constants : bool, default=True\n Whether to keep constants in the unwrapped expression\nreturn_self : bool, default=True\n Whether to return self if no unwrapping is needed\napply_scaling : bool, default=False\n Whether to automatically apply scale factors to variables with units\n\nExample:\n model = uw.Model()\n model.set_reference_quantities(mantle_temperature=1500*uw.scaling.units.K)\n\n temperature = uw.discretisation.MeshVariable(\"T\", mesh, 1, units=\"K\")\n expr = uw.function.expression(\"heat\", 2 * temperature.sym, \"heat equation\")\n\n # Normal unwrap\n result = uw.unwrap(expr)\n\n # Unwrap with automatic scaling\n scaled_result = uw.unwrap(expr, apply_scaling=True)", - "harvested_comments": [ - "Normal unwrap", - "Unwrap with automatic scaling" - ], - "status": "partial", + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "InMemoryBackend", "is_public": true }, { - "name": "synchronised_array_update", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 542, - "signature": "(context_info = 'user operations')", + "name": "load_vector", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 61, + "signature": "(self, key: str) -> np.ndarray", "parameters": [ { - "name": "context_info", - "type_hint": null, - "default": "'user operations'", + "name": "key", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Context manager for synchronised array updates across multiple variables.\n\nBatches multiple array assignments together and defers PETSc synchronization\nuntil the end of the context, ensuring atomic updates and better performance.\n\nExample\n-------\nwith uw.synchronised_array_update():\n velocity.array[...] = new_velocity_values\n pressure.array[...] = new_pressure_values\n temperature.array[...] = new_temperature_values\n# All arrays are synchronized here\n\nParameters\n----------\ncontext_info : str\n Optional description of the update context for debugging\n\nReturns\n-------\nContext manager for delayed callback execution", - "harvested_comments": [ - "All arrays are synchronized here" + "returns": "np.ndarray", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "InMemoryBackend", "is_public": true }, { - "name": "create_metric", - "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 88, - "signature": "(mesh: 'Mesh', h_values: np.ndarray, name: str = None) -> 'MeshVariable'", + "name": "save_metadata", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 66, + "signature": "(self, key: str, value: Any) -> None", "parameters": [ { - "name": "mesh", - "type_hint": "'Mesh'", + "name": "key", + "type_hint": "str", "default": null, "description": "" }, { - "name": "h_values", - "type_hint": "np.ndarray", + "name": "value", + "type_hint": "Any", "default": null, "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": "None", - "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Create adaptation metric from target edge lengths.\n\nThis is the core utility that converts target edge lengths (h-field) to\nthe metric tensor format required by MMG/PETSc mesh adaptation.\n\nParameters\n----------\nmesh : Mesh\n The mesh to create the metric on.\nh_values : np.ndarray\n Array of target edge lengths at each mesh node. Shape should be\n (n_nodes,) or (n_nodes, 1).\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"adaptation_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing metric values ready for mesh.adapt().\n\nNotes\n-----\n**Metric Tensor Mathematics**\n\nFor isotropic mesh adaptation, MMG/PETSc uses a metric tensor:\n\n.. math::\n\n M = h^{-2} \\cdot I\n\nwhere :math:`h` is the target edge length and :math:`I` is the identity\nmatrix. This relationship is **dimension-independent** - the same formula\napplies in 2D and 3D.\n\nHigher metric values produce smaller elements. The adaptation algorithm\nseeks to make :math:`\\mathbf{e}^T M \\mathbf{e} = 1` for all edges.\n\nExamples\n--------\n>>> # Create metric from h-field computed elsewhere\n>>> h_field = compute_error_based_h(solution) # User function\n>>> metric = uw.adaptivity.create_metric(mesh, h_field)\n>>> mesh.adapt(metric)\n\nSee Also\n--------\nmetric_from_gradient : Create metric from scalar field gradient.\nmetric_from_field : Create metric from indicator field.", - "harvested_comments": [ - "Create metric from h-field computed elsewhere", - "User function", - "Ensure h_values is the right shape", - "Create metric MeshVariable", - "Convert to metric tensor: M = 1/h\u00b2 \u00d7 I (isotropic)" + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "InMemoryBackend", "is_public": true }, { - "name": "metric_from_gradient", - "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 159, - "signature": "(field: 'MeshVariable', h_min: float, h_max: float, gradient_min: float = None, gradient_max: float = None, profile: str = 'linear', name: str = None) -> 'MeshVariable'", + "name": "load_metadata", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 71, + "signature": "(self, key: str) -> Any", "parameters": [ { - "name": "field", - "type_hint": "'MeshVariable'", - "default": null, - "description": "" - }, - { - "name": "h_min", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "h_max", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "gradient_min", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "gradient_max", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "profile", - "type_hint": "str", - "default": "'linear'", - "description": "" - }, - { - "name": "name", + "name": "key", "type_hint": "str", - "default": "None", + "default": null, "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Create adaptation metric from gradient of a scalar field.\n\nProduces a metric that refines where gradients are steep (high |\u2207\u03c6|)\nand coarsens where the field is smooth (low |\u2207\u03c6|). This is the standard\napproach for error-driven or feature-based mesh adaptation.\n\nParameters\n----------\nfield : MeshVariable\n Scalar MeshVariable whose gradient drives refinement. Must have\n num_components=1.\nh_min : float\n Target edge length where gradient is highest (finest mesh).\nh_max : float\n Target edge length where gradient is lowest (coarsest mesh).\ngradient_min : float, optional\n Gradient magnitude below this uses h_max. If None, uses 5th percentile\n of |\u2207\u03c6| values.\ngradient_max : float, optional\n Gradient magnitude above this uses h_min. If None, uses 95th percentile\n of |\u2207\u03c6| values.\nprofile : str, optional\n Interpolation profile: \"linear\", \"smoothstep\", or \"power\" (default: \"linear\").\n - \"linear\": h varies linearly with gradient magnitude\n - \"smoothstep\": smooth S-curve transition (C\u00b9 continuous)\n - \"power\": h \u221d |\u2207\u03c6|^(-1/2), natural for error equidistribution\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"{field.name}_gradient_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing metric values ready for mesh.adapt().\n\nNotes\n-----\n**Gradient-Based Refinement Strategy**\n\nThe idea is that steep gradients indicate regions where the solution is\nchanging rapidly - these need finer resolution to capture accurately.\nSmooth regions can use coarser mesh without losing accuracy.\n\nThe mapping is:\n - High |\u2207\u03c6| \u2192 small h \u2192 large metric \u2192 finer mesh\n - Low |\u2207\u03c6| \u2192 large h \u2192 small metric \u2192 coarser mesh\n\n**Choosing h_min and h_max**\n\n- ``h_min`` controls finest resolution (where gradients are steepest)\n- ``h_max`` controls coarsest resolution (smooth regions)\n- Ratio ``h_max/h_min`` gives refinement factor (e.g., 10 = 10\u00d7 finer at peaks)\n\n**Auto-detection of Gradient Range**\n\nIf ``gradient_min`` and ``gradient_max`` are not specified, they are\ncomputed from the actual gradient field:\n - gradient_min = 5th percentile of |\u2207\u03c6|\n - gradient_max = 95th percentile of |\u2207\u03c6|\n\nThis ensures robust behavior even when gradient magnitudes span many\norders of magnitude.\n\n**Implementation Note**\n\nGradients are computed using the Clement interpolant via\n``uw.function.evaluate(field.sym.diff(x), coords)``. This uses PETSc's\n``DMPlexComputeGradientClementInterpolant`` which averages cell-wise\ngradients at vertices. The result is O(h) accurate and fast (no linear\nsolve required).\n\nExamples\n--------\n>>> # Refine based on temperature gradient\n>>> metric = uw.adaptivity.metric_from_gradient(\n... T, h_min=0.005, h_max=0.05, profile=\"smoothstep\"\n... )\n>>> mesh.adapt(metric)\n\n>>> # Refine based on strain rate\n>>> # First compute strain rate magnitude as scalar field\n>>> SR = uw.discretisation.MeshVariable(\"SR\", mesh, 1)\n>>> # ... populate SR with strain rate second invariant ...\n>>> metric = uw.adaptivity.metric_from_gradient(SR, h_min=0.01, h_max=0.1)\n>>> mesh.adapt(metric)\n\nSee Also\n--------\ncreate_metric : Create metric from h-field directly.\nmetric_from_field : Create metric from indicator field (not gradient).", + "returns": "Any", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "InMemoryBackend", + "is_public": true + }, + { + "name": "list_vectors", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 76, + "signature": "(self) -> list[str]", + "parameters": [], + "returns": "list[str]", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "InMemoryBackend", + "is_public": true + }, + { + "name": "list_metadata", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 79, + "signature": "(self) -> list[str]", + "parameters": [], + "returns": "list[str]", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "InMemoryBackend", + "is_public": true + }, + { + "name": "state", + "kind": "property", + "file": "src/underworld3/checkpoint/state.py", + "line": 77, + "signature": "(self) -> SnapshottableState", + "parameters": [], + "returns": "SnapshottableState", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Snapshottable", + "is_public": true + }, + { + "name": "state", + "kind": "property", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 124, + "signature": "(self) -> TrackerState", + "parameters": [], + "returns": "TrackerState", + "existing_docstring": null, "harvested_comments": [ - "Refine based on temperature gradient", - "Refine based on strain rate", - "First compute strain rate magnitude as scalar field", - "... populate SR with strain rate second invariant ...", - "Compute gradient at mesh nodes using Clement interpolant" + "Deep-copy on read so a held .state is isolated from later", + "mutation even if not routed through the snapshot machinery." ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ModelTracker", "is_public": true }, { - "name": "metric_from_field", - "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 319, - "signature": "(indicator: 'MeshVariable', h_min: float, h_max: float, indicator_min: float = None, indicator_max: float = None, invert: bool = False, profile: str = 'linear', name: str = None) -> 'MeshVariable'", + "name": "state", + "kind": "method", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 130, + "signature": "(self, s: TrackerState) -> None", "parameters": [ { - "name": "indicator", - "type_hint": "'MeshVariable'", + "name": "s", + "type_hint": "TrackerState", "default": null, "description": "" - }, - { - "name": "h_min", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "h_max", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "indicator_min", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "indicator_max", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "invert", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "profile", - "type_hint": "str", - "default": "'linear'", - "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": "None", - "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Create adaptation metric from an indicator field.\n\nMaps a scalar indicator field (e.g., error estimate, phase field, distance)\nto target edge lengths. This is more general than gradient-based adaptation -\nyou provide any field indicating where refinement is needed.\n\nParameters\n----------\nindicator : MeshVariable\n Scalar field indicating where refinement is needed. Higher values\n (by default) produce finer mesh.\nh_min : float\n Target edge length where indicator is highest (finest mesh).\nh_max : float\n Target edge length where indicator is lowest (coarsest mesh).\nindicator_min : float, optional\n Indicator values below this use h_max. If None, uses field minimum.\nindicator_max : float, optional\n Indicator values above this use h_min. If None, uses field maximum.\ninvert : bool, optional\n If True, high indicator values \u2192 coarse mesh (swap h_min/h_max roles).\n Useful when indicator represents \"smoothness\" rather than \"need for\n refinement\". Default: False.\nprofile : str, optional\n Interpolation profile: \"linear\" or \"smoothstep\". Default: \"linear\".\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"{indicator.name}_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing metric values ready for mesh.adapt().\n\nNotes\n-----\n**Use Cases**\n\n- **Error estimates**: Pass a computed error field; high error \u2192 fine mesh\n- **Phase fields**: Refine at interfaces (|\u03c6| near transition value)\n- **Distance fields**: Refine near surfaces (use with Surface.distance)\n- **Material boundaries**: Refine near composition gradients\n\n**Relationship to Surface.refinement_metric()**\n\nThis function is a general-purpose version. Surface.refinement_metric()\nis a specialized wrapper that computes the indicator from surface distance.\n\nExamples\n--------\n>>> # Refine based on error estimate\n>>> error = compute_error_estimate(solution) # User function\n>>> metric = uw.adaptivity.metric_from_field(error, h_min=0.005, h_max=0.05)\n>>> mesh.adapt(metric)\n\n>>> # Refine at phase boundaries (\u03c6 transitions from 0 to 1)\n>>> # Want fine mesh where \u03c6 is near 0.5\n>>> phi_interface = 1 - 4 * (phi - 0.5)**2 # Peak at \u03c6=0.5\n>>> metric = uw.adaptivity.metric_from_field(phi_interface, h_min=0.01, h_max=0.1)\n\nSee Also\n--------\ncreate_metric : Create metric from h-field directly.\nmetric_from_gradient : Create metric from field gradient.", + "returns": "None", + "existing_docstring": null, "harvested_comments": [ - "Refine based on error estimate", - "User function", - "Refine at phase boundaries (\u03c6 transitions from 0 to 1)", - "Want fine mesh where \u03c6 is near 0.5", - "Peak at \u03c6=0.5" + "Replace wholesale: restore returns to exactly the captured", + "point, so a quantity added *after* the snapshot is dropped", + "on restore (git-stash semantics)." ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ModelTracker", "is_public": true }, { - "name": "mesh2mesh_swarm", - "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 549, - "signature": "(mesh0, mesh1, swarm0, swarmVarList, proxy = True, verbose = False)", - "parameters": [ - { - "name": "mesh0", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "mesh1", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "swarm0", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "swarmVarList", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "proxy", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "name": "viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 158, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Warning [NSFW] - this uses EXPLICIT message passing calls to handle the\nsituation where a swarm cell_dm cannot find particles after mesh redistribution.\nThis occurs when particles are moved accross non-neighbouring processes or if the\nmesh neighbours are redistricted. This should be fixed at the DMSwarm / DMPlex level\nso this code is just a placeholder. Or maybe it's just user error !\n\nNotes 1: This copies a swarm from one mesh to another allowing for completely incommensurate\npartitionings. Warning: this is not always a 1->1 mapping. There may be some duplication and\nparticles may go missing along curved boundaries where the meshes do not necessarily overlap.\nThe same is true in the shadow spaces.\n\nNote 2: The swarm is \"adapted\" to the original mesh and will need\nto be repopulated on the new one, or data can be mapped to a purpose-built swarm.\n\nNote 3: We pass the data around as floats for the time being. Be careful when converting back.\n\nNote 4: set proxy=True to automatically generate proxy variables on mesh1 but consider skipping\nif the returned swarm is ephemeral", - "harvested_comments": [ - "f\"{uw.mpi.rank} - A/local found: {n_found} v. not found: {n_not_found}\",", - "flush=False,", - "Let's sync the number of missing points by rank", - "print(f\"rank: {uw.mpi.rank}, local_array size: {n_not_found}\")", - "f\"{uw.mpi.rank} Sizes are: {global_sizes} Buffer size: {global_size}\"," - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_ViscousParameterAlias", "is_public": true }, { - "name": "mesh2mesh_meshVariable", - "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 790, - "signature": "(meshVar0, meshVar1, verbose = False)", + "name": "viscosity", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 162, + "signature": "(self, value)", "parameters": [ { - "name": "meshVar0", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "meshVar1", + "name": "value", "type_hint": null, "default": null, "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Map a meshVar on mesh0 to a meshVar on mesh1 using\nan intermediary (temporary) swarm", - "harvested_comments": [ - "1 Create a temporary swarm with a variable that matches meshVar0", - "Maybe 3+ if var is higher order ??", - "Set data on the swarmVar", - "print(f\"Map data to swarm (rbf) - points = {tmp_swarm.dm.getSize()}\", flush=True)", - "print(f\"Distribute swarm\", flush=True)" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_ViscousParameterAlias", "is_public": true }, { - "name": "build_index", + "name": "yield_mode", "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 155, - "signature": "def build_index(self):", - "parameters": [], + "file": "src/underworld3/constitutive_models.py", + "line": 1958, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "\n Build the kd-tree index.\n", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "validate_parameters", - "kind": "function", + "name": "yield_softness", + "kind": "method", "file": "src/underworld3/constitutive_models.py", - "line": 60, - "signature": "(symbol, input, default = None, allow_number = True, allow_expression = True)", + "line": 1986, + "signature": "(self, value)", "parameters": [ { - "name": "symbol", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "input", + "name": "value", "type_hint": null, "default": null, "description": "" - }, - { - "name": "default", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "allow_number", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "allow_expression", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, - "existing_docstring": "Convert input to a UWexpression for use in constitutive models.\n\nParameters\n----------\nsymbol : str\n LaTeX symbol for display (e.g., r\"\\eta\" for viscosity).\ninput : various\n Value to convert (UWexpression, UWQuantity, float, int, sympy expr).\ndefault : optional\n Default value if input is None.\nallow_number : bool\n If True, accept plain numbers (int/float).\nallow_expression : bool\n If True, accept raw sympy expressions.\n\nReturns\n-------\nUWexpression or None\n Wrapped expression, or None if conversion failed.", - "harvested_comments": [ - "CRITICAL: Check for UWexpression FIRST, before checking sympy.Basic", - "UWexpression inherits from sympy.Symbol, so it would match the Basic check", - "and cause double-wrapping, losing unit information", - "Already a UWexpression - return as-is, no wrapping needed", - "Convert UWQuantity to UWexpression - this is the beautiful symmetry!" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "create_unique_symbol", + "name": "dt_elastic", "kind": "method", "file": "src/underworld3/constitutive_models.py", - "line": 229, - "signature": "(self, base_symbol, value, description)", + "line": 2845, + "signature": "(inner_self, value)", "parameters": [ { - "name": "base_symbol", + "name": "inner_self", "type_hint": null, "default": null, "description": "" @@ -8716,464 +8294,319 @@ "type_hint": null, "default": null, "description": "" - }, - { - "name": "description", - "type_hint": null, - "default": null, - "description": "" } ], "returns": null, - "existing_docstring": "Create a unique symbol name for constitutive model parameters.\n\nSymbol naming priority:\n1. If material_name is set: \u03b7 \u2192 \u03b7_{material_name}\n2. Else if multiple instances of same class: \u03b7 \u2192 \u03b7^{(n)}\n3. Else: use base symbol as-is\n\nParameters\n----------\nbase_symbol : str\n The base LaTeX symbol name (e.g., r\"\\eta\", r\"\\kappa\")\nvalue : float or expression\n The initial value for the symbol\ndescription : str\n Description of the parameter\n\nReturns\n-------\nUWexpression\n Expression with unique symbol name", - "harvested_comments": [ - "Priority 1: User-specified material name (subscript notation)", - "Priority 2: Multiple instances of same class (superscript notation)", - "Priority 3: First/only instance - clean symbol" - ], - "status": "complete", - "needs": [], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "Unknowns", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 275, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Reference to the solver's unknown fields.\n\nReturns\n-------\nUnknowns\n Container holding the primary unknown field(s) (e.g., velocity,\n pressure, temperature) that this constitutive model operates on.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "_Parameters", "is_public": true }, { - "name": "Unknowns", + "name": "bdf_blend", "kind": "method", "file": "src/underworld3/constitutive_models.py", - "line": 288, - "signature": "(self, unknowns)", + "line": 3094, + "signature": "(self, value)", "parameters": [ { - "name": "unknowns", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set the solver unknowns (invalidates setup).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "K", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 295, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Primary constitutive property (viscosity, diffusivity, etc.).\n\nReturns\n-------\nUWexpression\n The material property defining the flux-gradient relationship.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "u", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 306, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "The primary unknown field from the solver.\n\nReturns\n-------\nMeshVariable\n The unknown field (velocity, temperature, etc.).", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "grad_u", - "kind": "property", + "name": "yield_mode", + "kind": "method", "file": "src/underworld3/constitutive_models.py", - "line": 317, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Gradient of the unknown field.\n\nFor scalar fields, this is a vector. For vector fields (velocity),\nthis is the velocity gradient tensor :math:`\\nabla \\mathbf{u}`.\n\nReturns\n-------\nsympy.Matrix\n Gradient/Jacobian of the unknown field.", - "harvested_comments": [ - "return mesh.vector.gradient(self.Unknowns.u.sym)" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "line": 3465, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "DuDt", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 333, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Material derivative operator for the unknown field.\n\nUsed in time-dependent problems to track Lagrangian or\nsemi-Lagrangian derivatives.\n\nReturns\n-------\nSemiLagrangian_DDt or Lagrangian_DDt or None\n The material derivative operator, or None if not set.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "DuDt", + "name": "yield_softness", "kind": "method", "file": "src/underworld3/constitutive_models.py", - "line": 347, - "signature": "(self, DuDt_value: Union[SemiLagrangian_DDt, Lagrangian_DDt])", + "line": 3485, + "signature": "(self, value)", "parameters": [ { - "name": "DuDt_value", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "name": "value", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set the material derivative operator for the unknown.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "DFDt", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 357, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Material derivative operator for the flux history.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "DFDt", + "name": "tau_par_cap_factor", "kind": "method", "file": "src/underworld3/constitutive_models.py", - "line": 363, - "signature": "(self, DFDt_value: Union[SemiLagrangian_DDt, Lagrangian_DDt])", + "line": 3622, + "signature": "(self, value)", "parameters": [ { - "name": "DFDt_value", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "name": "value", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set the material derivative operator for flux history.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "TransverseIsotropicVEPSplitFlowModel", "is_public": true }, { - "name": "C", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 375, - "signature": "(self)", + "name": "petsc_dm_get_periodicity", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 251, + "signature": "def petsc_dm_get_periodicity(incoming_dm):", "parameters": [], "returns": null, - "existing_docstring": "The matrix form of the constitutive model (the `c` property)\nthat relates fluxes to gradients.\nFor scalar problem, this is the matrix representation of the rank 2 tensor.\nFor vector problems, the Mandel form of the rank 4 tensor is returned.\nNOTE: this is an immutable object that is _a view_ of the underlying tensor", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": true }, { - "name": "c", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 394, - "signature": "(self)", + "name": "petsc_vec_concatenate", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 324, + "signature": "def petsc_vec_concatenate( inputVecs ):", "parameters": [], "returns": null, - "existing_docstring": "The tensor form of the constitutive model that relates fluxes to gradients. In scalar\nproblems, `c` and `C` are equivalent (matrices), but in vector problems, `c` is a\nrank 4 tensor. NOTE: `c` is the canonical form of the constitutive relationship.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": true }, { - "name": "flux", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 408, - "signature": "(self)", + "name": "petsc_get_swarm_coord_name", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 347, + "signature": "def petsc_get_swarm_coord_name( sdm ):", "parameters": [], "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": true }, { - "name": "flux_1d", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 439, - "signature": "(self)", + "name": "has_meaningful_units", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 148, + "signature": "def has_meaningful_units(u):", "parameters": [], "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux. Returns the Voigt form that is flattened so as to\nmatch the PETSc field storage pattern for symmetric tensors.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": true }, { - "name": "viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 605, - "signature": "(self)", + "name": "has_meaningful_units", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 482, + "signature": "def has_meaningful_units(u):", "parameters": [], "returns": null, - "existing_docstring": "Whatever the consistutive model defines as the effective value of viscosity\nin the form of an uw.expression", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscousFlowModel", + "parent_class": null, "is_public": true }, { - "name": "K", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 612, - "signature": "(self)", - "parameters": [], + "name": "extend_enum", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 82, + "signature": "(inherited)", + "parameters": [ + { + "name": "inherited", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Effective stiffness parameter (viscosity for viscous flow)", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscousFlowModel", + "parent_class": null, "is_public": true }, { - "name": "flux", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 617, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Viscous stress tensor: :math:`\\boldsymbol{\\tau} = 2\\eta\\dot{\\varepsilon}`.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "ViscousFlowModel", - "is_public": true - }, - { - "name": "grad_u", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 646, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Symmetric strain rate tensor (with 1/2 factor).\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "ViscousFlowModel", - "is_public": true - }, - { - "name": "viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 851, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Effective viscosity with plastic yielding.\n\n.. math::\n \\eta_{\\mathrm{eff}} = \\min\\left(\\eta_0, \\frac{\\tau_y}{2\\dot{\\varepsilon}_{II}}\\right)\n\nwhere :math:`\\dot{\\varepsilon}_{II}` is the second invariant of strain rate.", - "harvested_comments": [ - "detect if values we need are defined or are placeholder symbols", - "Don't put conditional behaviour in the constitutive law", - "when it is not needed", - "# Question is, will sympy reliably differentiate something", - "# with so many Max / Min statements. The smooth version would" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "ViscoPlasticFlowModel", - "is_public": true - }, - { - "name": "plastic_correction", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 901, - "signature": "(self) -> float", - "parameters": [], - "returns": "float", - "existing_docstring": "Scaling factor to reduce stress to yield surface.\n\n.. math::\n f = \\frac{\\tau_y}{\\tau_{II}}\n\nwhere :math:`\\tau_{II}` is the second invariant of deviatoric stress.\nReturns 1 if no yield stress is set.", - "harvested_comments": [ - "The yield criterion in this case is assumed to be a bound on the second invariant of the stress" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "ViscoPlasticFlowModel", - "is_public": true - }, - { - "name": "ve_effective_viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1110, - "signature": "(inner_self)", + "name": "checkpoint_xdmf", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 6232, + "signature": "(filename: str, meshUpdates: bool = True, meshVars: Optional[list] = [], swarmVars: Optional[list] = [], index: Optional[int] = 0)", "parameters": [ { - "name": "inner_self", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" + }, + { + "name": "meshUpdates", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "meshVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "swarmVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "index", + "type_hint": "Optional[int]", + "default": "0", + "description": "" } ], "returns": null, - "existing_docstring": "Visco-elastic effective viscosity: :math:`\\eta_{\\mathrm{eff}} = \\frac{\\eta G \\Delta t}{\\eta + G \\Delta t}`.", + "existing_docstring": null, "harvested_comments": [ - "the dt_elastic defaults to infinity, t_relax to zero,", - "so this should be well behaved in the viscous limit", - "Note, 1st order only here but we should add higher order versions of this", - "1st Order version (default)", - "2nd Order version (need to ask for this one)" + "# Identify the mesh file. Use the", + "# zeroth one if this option is turned off", + "# Obtain the mesh information", + "We only use a subset of the possible cell types", + "# Create the header" ], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": null, "is_public": true }, { - "name": "t_relax", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1150, - "signature": "(inner_self)", + "name": "extend_enum", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 61, + "signature": "(inherited)", "parameters": [ { - "name": "inner_self", + "name": "inherited", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Maxwell relaxation time: :math:`t_{\\mathrm{relax}} = \\eta / G`.", - "harvested_comments": [ - "shear modulus defaults to infinity so t_relax goes to zero", - "in the viscous limit" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "_Parameters", - "is_public": true - }, - { - "name": "order", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1161, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Time integration order (1 or 2).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": null, "is_public": true }, { - "name": "order", + "name": "remesh_policy", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1166, + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 523, "signature": "(self, value)", "parameters": [ { @@ -9184,10390 +8617,36855 @@ } ], "returns": null, - "existing_docstring": "Set the time integration order.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "stress_star", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1174, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Previous timestep stress :math:`\\boldsymbol{\\sigma}^*` from history.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "load_from_h5_plex_vector", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1455, + "signature": "(self, filename: str, data_name: Optional[str] = None)", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "data_name", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + } ], - "parent_class": "ViscoElasticPlasticFlowModel", - "is_public": true - }, - { - "name": "stress_2star", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1182, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Second-order stress history :math:`\\boldsymbol{\\sigma}^{**}` (for 2nd order integration).", + "existing_docstring": null, "harvested_comments": [ - "Check if we have enough information in DFDt to update _stress_star,", - "otherwise it will be defined as zero" + "Ensure vectors are initialized" ], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "E_eff", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1196, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Effective strain rate including elastic contribution.\n\n.. math::\n \\dot{\\varepsilon}_{\\mathrm{eff}} = \\dot{\\varepsilon} + \\frac{\\boldsymbol{\\sigma}^*}{2 G \\Delta t}", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "name": "remesh_policy", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 378, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "ViscoElasticPlasticFlowModel", - "is_public": true - }, - { - "name": "E_eff_inv_II", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1227, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Second invariant of effective strain rate: :math:`\\dot{\\varepsilon}_{II} = \\sqrt{\\frac{1}{2}\\dot{\\varepsilon}_{ij}\\dot{\\varepsilon}_{ij}}`.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "K", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1235, - "signature": "(self)", + "name": "eval", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 69, + "signature": "def eval(cls, *args ):", "parameters": [], "returns": null, - "existing_docstring": "Effective stiffness parameter (viscosity for visco-elastic-plastic flow).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": null, "is_public": true }, { - "name": "viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1240, - "signature": "(self)", + "name": "eval", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 87, + "signature": "def eval(cls, *args ):", "parameters": [], "returns": null, - "existing_docstring": "Effective viscosity combining visco-elastic and plastic limits.\n\nReturns :math:`\\min(\\eta_{\\mathrm{ve}}, \\tau_y / 2\\dot{\\varepsilon}_{II})`.", - "harvested_comments": [ - "detect if values we need are defined or are placeholder symbols", - "# Do we want this to be an expression of its own ? If so, define above in __init__() and", - "# make sure it is updated in this call, rather than being replaced.", - "# Why is it p**2 here ?", - "p = self.plastic_correction()" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": null, "is_public": true }, { - "name": "plastic_correction", + "name": "eval", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1318, - "signature": "(self)", + "file": "src/underworld3/function/analytic.pyx", + "line": 122, + "signature": "def eval(cls, *args ):", "parameters": [], "returns": null, - "existing_docstring": "Scaling factor to reduce stress to yield surface: :math:`f = \\tau_y / \\tau_{II}`.", - "harvested_comments": [ - "The yield criterion in this case is assumed to be a bound on the second invariant of the stress" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": null, "is_public": true }, { - "name": "flux", + "name": "description", "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1376, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux. For viscoelasticity, the", - "harvested_comments": [ - "if self.is_viscoplastic:", - "plastic_scale_factor = sympy.Max(1, self.plastic_overshoot())", - "stress /= plastic_scale_factor" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "ViscoElasticPlasticFlowModel", - "is_public": true - }, - { - "name": "stress_projection", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1392, + "file": "src/underworld3/function/expressions.py", + "line": 996, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "viscoelastic stress projection (no plastic response)", - "harvested_comments": [ - "This is a scalar viscosity ..." - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "UWexpression", "is_public": true }, { - "name": "stress", + "name": "description", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1416, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "viscoelastic stress projection (no plastic response)", - "harvested_comments": [ - "This is a scalar viscosity ..." - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "ViscoElasticPlasticFlowModel", - "is_public": true - }, - { - "name": "is_elastic", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1513, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "True if elastic behavior is active (finite dt_elastic and shear_modulus).", - "harvested_comments": [ - "If any of these is not defined, elasticity is switched off" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/function/expressions.py", + "line": 1000, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "ViscoElasticPlasticFlowModel", - "is_public": true - }, - { - "name": "is_viscoplastic", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1526, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "True if plastic yielding is active (finite yield_stress).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "UWexpression", "is_public": true }, { - "name": "K", + "name": "mesh", "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1610, + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 114, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Diffusivity :math:`\\kappa` (alias for ``diffusivity``).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "DiffusionModel", + "parent_class": "BoundingSurface", "is_public": true }, { - "name": "diffusivity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1615, - "signature": "(self)", - "parameters": [], + "name": "ensure_to_base_units", + "kind": "function", + "file": "src/underworld3/scaling/_utils.py", + "line": 20, + "signature": "(val)", + "parameters": [ + { + "name": "val", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Scalar or tensor diffusivity :math:`\\kappa`.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "DiffusionModel", + "parent_class": null, "is_public": true }, { - "name": "diffusivity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1684, - "signature": "(inner_self)", + "name": "get", + "kind": "method", + "file": "src/underworld3/scaling/_utils.py", + "line": 48, + "signature": "(self, k, default = None)", "parameters": [ { - "name": "inner_self", + "name": "k", "type_hint": null, "default": null, "description": "" + }, + { + "name": "default", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Diagonal diffusivity tensor.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "TransformedDict", "is_public": true }, { - "name": "diffusivity", + "name": "setdefault", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1689, - "signature": "(inner_self, value: sympy.Matrix)", + "file": "src/underworld3/scaling/_utils.py", + "line": 51, + "signature": "(self, k, default = None)", "parameters": [ { - "name": "inner_self", + "name": "k", "type_hint": null, "default": null, "description": "" }, { - "name": "value", - "type_hint": "sympy.Matrix", - "default": null, + "name": "default", + "type_hint": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Set diffusivity from a vector of per-direction values.", - "harvested_comments": [ - "Accept shape (dim, 1) or (1, dim)", - "Validate each component using validate_parameters", - "Store the validated diffusivity as a diagonal matrix" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "TransformedDict", "is_public": true }, { - "name": "flux", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1759, - "signature": "(inner_self)", + "name": "pop", + "kind": "method", + "file": "src/underworld3/scaling/_utils.py", + "line": 54, + "signature": "(self, k, v = _RaiseKeyError)", "parameters": [ { - "name": "inner_self", + "name": "k", "type_hint": null, "default": null, "description": "" + }, + { + "name": "v", + "type_hint": null, + "default": "_RaiseKeyError", + "description": "" } ], "returns": null, - "existing_docstring": "User-defined flux expression.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "TransformedDict", "is_public": true }, { - "name": "flux", + "name": "update", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1764, - "signature": "(inner_self, value: sympy.Matrix)", + "file": "src/underworld3/scaling/_utils.py", + "line": 59, + "signature": "(self, mapping = (), **kwargs)", "parameters": [ { - "name": "inner_self", + "name": "mapping", "type_hint": null, - "default": null, + "default": "()", "description": "" }, { - "name": "value", - "type_hint": "sympy.Matrix", + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set the flux expression (must be a vector of length dim).", - "harvested_comments": [ - "Accept shape (dim, 1) or (1, dim)", - "Flatten and validate" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "TransformedDict", "is_public": true }, { - "name": "flux", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1788, + "name": "copy", + "kind": "method", + "file": "src/underworld3/scaling/_utils.py", + "line": 65, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "The user-defined flux expression.", + "existing_docstring": null, "harvested_comments": [ - "if self._flux is None:", - "raise RuntimeError(\"Flux expression has not been set.\")" + "don't delegate w/ super - dict.copy() -> dict :(" ], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GenericFluxModel", + "parent_class": "TransformedDict", "is_public": true }, { - "name": "s", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1890, - "signature": "(inner_self)", + "name": "fromkeys", + "kind": "method", + "file": "src/underworld3/scaling/_utils.py", + "line": 69, + "signature": "(cls, keys, v = None)", "parameters": [ { - "name": "inner_self", + "name": "keys", "type_hint": null, "default": null, "description": "" + }, + { + "name": "v", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Body force vector (e.g., gravitational source term :math:`\\rho \\mathbf{g}`).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "TransformedDict", "is_public": true }, { - "name": "s", + "name": "write_proxy", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1895, - "signature": "(inner_self, value: sympy.Matrix)", + "file": "src/underworld3/swarm.py", + "line": 2032, + "signature": "(self, filename: str)", "parameters": [ { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "sympy.Matrix", + "name": "filename", + "type_hint": "str", "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set the body force vector.", + "existing_docstring": null, "harvested_comments": [ - "Update expression content in-place to preserve object identity", - "Cannot use validate_parameters() as it doesn't handle matrices", - "UWexpression.sym setter handles sympy.Matrix directly" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "_Parameters", - "is_public": true - }, - { - "name": "K", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1904, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Permeability :math:`\\kappa` [m\u00b2] - the primary constitutive parameter.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "DarcyFlowModel", - "is_public": true - }, - { - "name": "flux", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1947, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "DarcyFlowModel", - "is_public": true - }, - { - "name": "viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 2052, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Whatever the consistutive model defines as the effective value of viscosity\nin the form of an uw.expression", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "if not proxied, nothing to do. return." ], - "parent_class": "TransverseIsotropicFlowModel", - "is_public": true - }, - { - "name": "K", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 2059, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Whatever the consistutive model defines as the effective value of viscosity\nin the form of an uw.expression", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "TransverseIsotropicFlowModel", - "is_public": true - }, - { - "name": "grad_u", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 2066, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Symmetric strain rate tensor (with 1/2 factor).\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "TransverseIsotropicFlowModel", + "parent_class": "SwarmVariable", "is_public": true }, { - "name": "flux", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 2265, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Compute level-set weighted average of constituent model fluxes.\n\nCRITICAL: This composite flux becomes the stress history that\nall constituent models (including elastic ones) will read via\n``DFDt.psi_star[0]`` in the next time step.", - "harvested_comments": [ - "Get reference flux shape from first model", - "Compute normalization factor to ensure partition of unity", - "Get normalized level-set function for material i", - "Get flux contribution from constituent model i", - "Add weighted contribution to composite flux" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "name": "read_timestep", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2043, + "signature": "(self, data_filename: str, swarmID: str, data_name: str, index: int, outputPath = '')", + "parameters": [ + { + "name": "data_filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "swarmID", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "data_name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "index", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "outputPath", + "type_hint": null, + "default": "''", + "description": "" + } ], - "parent_class": "MultiMaterialConstitutiveModel", - "is_public": true - }, - { - "name": "K", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 2305, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Effective stiffness using level-set weighted harmonic average.\n\nFor composite materials, harmonic averaging gives the correct effective\nstiffness for preconditioning: 1/K_eff = \u03a3(\u03c6\u1d62 * (1/K\u1d62)) / \u03a3(\u03c6\u1d62)", + "existing_docstring": null, "harvested_comments": [ - "Harmonic average: 1/K_eff = \u03a3(\u03c6\u1d62 * (1/K\u1d62)) / \u03a3(\u03c6\u1d62)", - "Compute normalization factor to ensure partition of unity", - "Get normalized level-set function for material i", - "Get stiffness from constituent model i", - "Add weighted contribution to inverse stiffness" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "mesh.write_timestep( \"test\", meshUpdates=False, meshVars=[X], outputPath=\"\", index=0)", + "swarm.write_timestep(\"test\", \"swarm\", swarmVars=[var], outputPath=\"\", index=0)", + "check if swarmFilename exists", + "easier to debug abs path", + "Memory-bounded parallel read. Rank 0 reads the saved file in one" ], - "parent_class": "MultiMaterialConstitutiveModel", - "is_public": true - }, - { - "name": "solver", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 144, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Each constitutive relationship can, optionally, be associated with one solver object.\nand a solver object _requires_ a constitive relationship to be defined.", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "C", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 159, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "The matrix form of the constitutive model (the `c` property)\nthat relates fluxes to gradients.\nFor scalar problem, this is the matrix representation of the rank 2 tensor.\nFor vector problems, the Mandel form of the rank 4 tensor is returned.\nNOTE: this is an immutable object that is _a view_ of the underlying tensor", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "Constitutive_Model", - "is_public": true - }, - { - "name": "c", - "kind": "property", - "file": "src/underworld3/constitutive_models_new.py", - "line": 176, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "The tensor form of the constitutive model that relates fluxes to gradients. In scalar\nproblems, `c` and `C` are equivalent (matrices), but in vector problems, `c` is a\nrank 4 tensor. NOTE: `c` is the canonical form of the constitutive relationship.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "Constitutive_Model", + "parent_class": "SwarmVariable", "is_public": true }, { - "name": "flux", + "name": "read_timestep", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 184, - "signature": "(self, ddu: sympy.Matrix = None, ddu_dt: sympy.Matrix = None, u: sympy.Matrix = None, u_dt: sympy.Matrix = None)", + "file": "src/underworld3/swarm.py", + "line": 4137, + "signature": "(self, base_filename: str, swarm_id: str, index: int, outputPath: Optional[str] = '', migrate = True)", "parameters": [ { - "name": "ddu", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "base_filename", + "type_hint": "str", + "default": null, "description": "" }, { - "name": "ddu_dt", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "swarm_id", + "type_hint": "str", + "default": null, "description": "" }, { - "name": "u", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "index", + "type_hint": "int", + "default": null, "description": "" }, { - "name": "u_dt", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "outputPath", + "type_hint": "Optional[str]", + "default": "''", + "description": "" + }, + { + "name": "migrate", + "type_hint": null, + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux.", + "existing_docstring": null, "harvested_comments": [ - "may be needed in the case of cylindrical / spherical", - "tensor multiplication" + "Keep-local restore: every rank reads the saved coordinates", + "(plain read-only h5py) and add_particles_with_coordinates", + "keeps only the points this rank owns \u2014 no communication, no", + "rank-0 memory hotspot. The cost is np-fold read amplification,", + "which scalable/striped parallel filesystems absorb, so this is" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "Swarm", "is_public": true }, { - "name": "flux_1d", + "name": "advection", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 210, - "signature": "(self, ddu: sympy.Matrix = None, ddu_dt: sympy.Matrix = None, u: sympy.Matrix = None, u_dt: sympy.Matrix = None)", + "file": "src/underworld3/swarm.py", + "line": 4839, + "signature": "(self, V_fn, delta_t, order = 2, corrector = False, restore_points_to_domain_func = None, evalf = False, step_limit = False)", "parameters": [ { - "name": "ddu", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "V_fn", + "type_hint": null, + "default": null, "description": "" }, { - "name": "ddu_dt", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "delta_t", + "type_hint": null, + "default": null, "description": "" }, { - "name": "u", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "order", + "type_hint": null, + "default": "2", "description": "" }, { - "name": "u_dt", - "type_hint": "sympy.Matrix", + "name": "corrector", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "restore_points_to_domain_func", + "type_hint": null, "default": "None", "description": "" + }, + { + "name": "evalf", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "step_limit", + "type_hint": null, + "default": "False", + "description": "" } ], "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux. Returns the Voigt form that is flattened so as to\nmatch the PETSc field storage pattern for symmetric tensors.", + "existing_docstring": null, "harvested_comments": [ - "may be needed in the case of cylindrical / spherical" + "Convert delta_t to model units if it has units", + "This ensures consistent arithmetic: velocity is in model units, so time must be too", + "X0 holds the particle location at the start of advection", + "This is needed because the particles may be migrated off-proc", + "during timestepping. Probably not needed - use global evaluation instead" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "Swarm", "is_public": true }, { - "name": "flux", + "name": "advection", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1328, - "signature": "(self, ddu: sympy.Matrix = None, ddu_dt: sympy.Matrix = None, u: sympy.Matrix = None, u_dt: sympy.Matrix = None)", + "file": "src/underworld3/swarm.py", + "line": 5191, + "signature": "(self, V_fn, delta_t, order = 2, corrector = False, restore_points_to_domain_func = None, evalf = False, step_limit = True)", "parameters": [ { - "name": "ddu", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "V_fn", + "type_hint": null, + "default": null, "description": "" }, { - "name": "ddu_dt", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "delta_t", + "type_hint": null, + "default": null, "description": "" }, { - "name": "u", - "type_hint": "sympy.Matrix", - "default": "None", + "name": "order", + "type_hint": null, + "default": "2", "description": "" }, { - "name": "u_dt", - "type_hint": "sympy.Matrix", + "name": "corrector", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "restore_points_to_domain_func", + "type_hint": null, "default": "None", "description": "" + }, + { + "name": "evalf", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "step_limit", + "type_hint": null, + "default": "True", + "description": "" } ], "returns": null, - "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux. For viscoelasticity, the", - "harvested_comments": [ - "may be needed in the case of cylindrical / spherical", - "tensor multiplication", - "Now add in the stress history. In the", - "viscous limit, this term is not well behaved", - "and we need to check that" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "NodalPointSwarm", "is_public": true }, { - "name": "sym", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 166, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Symbolic representation for JIT/symbolic operations.\n\nFor UWCoordinate (which IS a BaseScalar), this returns self.\nThis maintains API compatibility with code that accesses .sym", + "name": "state", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 979, + "signature": "(self, s: 'DDtEulerianState') -> None", + "parameters": [ + { + "name": "s", + "type_hint": "'DDtEulerianState'", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": "Eulerian", "is_public": true }, { - "name": "data", + "name": "state", "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 176, - "signature": "(self)", + "file": "src/underworld3/systems/ddt.py", + "line": 1884, + "signature": "(self) -> 'DDtSemiLagrangianState'", "parameters": [], - "returns": null, - "existing_docstring": "Coordinate values from mesh.\n\nReturns dimensional values if mesh has units, ND otherwise.\nMirrors MeshVariable.data pattern.\n\nReturns\n-------\nnumpy.ndarray\n Coordinate values for this axis at all mesh nodes", + "returns": "'DDtSemiLagrangianState'", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": "SemiLagrangian", "is_public": true }, { - "name": "mesh", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 193, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Parent mesh.", + "name": "state", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1899, + "signature": "(self, s: 'DDtSemiLagrangianState') -> None", + "parameters": [ + { + "name": "s", + "type_hint": "'DDtSemiLagrangianState'", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": "SemiLagrangian", "is_public": true }, { - "name": "axis", + "name": "state", "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 198, - "signature": "(self)", + "file": "src/underworld3/systems/ddt.py", + "line": 3085, + "signature": "(self) -> 'DDtLagrangianState'", "parameters": [], - "returns": null, - "existing_docstring": "Axis index (0=x, 1=y, 2=z).", + "returns": "'DDtLagrangianState'", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": "Lagrangian", "is_public": true }, { - "name": "units", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 208, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Units of this coordinate, delegated from the original BaseScalar.\n\nThe mesh's patch_coordinate_units() sets ._units on mesh.N.x, mesh.N.y, etc.\nUWCoordinate wraps these, so we delegate to get the units.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "name": "state", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3095, + "signature": "(self, s: 'DDtLagrangianState') -> None", + "parameters": [ + { + "name": "s", + "type_hint": "'DDtLagrangianState'", + "default": null, + "description": "" + } ], - "parent_class": "UWCoordinate", + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", "is_public": true }, { - "name": "uwdiff", - "kind": "function", - "file": "src/underworld3/coordinates.py", - "line": 260, - "signature": "(expr, *symbols)", + "name": "state", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 3442, + "signature": "(self) -> 'DDtLagrangianSwarmState'", + "parameters": [], + "returns": "'DDtLagrangianSwarmState'", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "state", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3452, + "signature": "(self, s: 'DDtLagrangianSwarmState') -> None", "parameters": [ { - "name": "expr", + "name": "s", + "type_hint": "'DDtLagrangianSwarmState'", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 512, + "signature": "(self_or_cls, class_documentation = False)", + "parameters": [ + { + "name": "self_or_cls", "type_hint": null, "default": null, "description": "" }, { - "name": "*symbols", + "name": "class_documentation", "type_hint": null, - "default": null, + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Differentiate an expression with respect to coordinates.\n\n.. deprecated:: December 2025\n Since UWCoordinate now subclasses BaseScalar with proper __eq__/__hash__,\n you can use ``sympy.diff(expr, y)`` directly. This function is kept for\n backward compatibility but simply delegates to sympy.diff().\n\nParameters\n----------\nexpr : sympy.Expr\n The expression to differentiate\nsymbols : UWCoordinate or sympy.Symbol\n The variables to differentiate with respect to\n\nReturns\n-------\nsympy.Expr\n The derivative\n\nExamples\n--------\n>>> x, y = mesh.X # UWCoordinates\n>>> v = mesh_variable # MeshVariable\n>>> # Both now work identically:\n>>> dv_dy = sympy.diff(v.sym[0], y) # Preferred\n>>> dv_dy = uw.uwdiff(v.sym[0], y) # Backward compatible", + "existing_docstring": null, "harvested_comments": [ - "UWCoordinates", - "MeshVariable", - "Both now work identically:", - "Backward compatible" + "# Docstring (static / class documentation)" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "uw_object", "is_public": true }, { - "name": "geographic_to_cartesian", + "name": "debugging_text", "kind": "function", - "file": "src/underworld3/coordinates.py", - "line": 385, - "signature": "(lon_deg, lat_deg, depth_km, a, b)", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 490, + "signature": "(randstr, fn, fn_type, eqn_no)", "parameters": [ { - "name": "lon_deg", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "lat_deg", + "name": "randstr", "type_hint": null, "default": null, "description": "" }, { - "name": "depth_km", + "name": "fn", "type_hint": null, "default": null, "description": "" }, { - "name": "a", + "name": "fn_type", "type_hint": null, "default": null, "description": "" }, { - "name": "b", + "name": "eqn_no", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert geographic coordinates to Cartesian coordinates.\n\nUses geodetic latitude (perpendicular to ellipsoid surface).\n\nParameters\n----------\nlon_deg : float or array\n Longitude in degrees East (-180 to 180 or 0 to 360)\nlat_deg : float or array\n Latitude in degrees North (-90 to 90), geodetic\ndepth_km : float or array\n Depth below ellipsoid surface in km (positive downward)\na : float\n Semi-major axis (equatorial radius) in km\nb : float\n Semi-minor axis (polar radius) in km\n\nReturns\n-------\nx, y, z : float or array\n Cartesian coordinates in km", - "harvested_comments": [ - "Convert to radians", - "Eccentricity squared", - "Prime vertical radius of curvature at this latitude", - "Height above ellipsoid (negative of depth)", - "Cartesian coordinates" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": true }, { - "name": "cartesian_to_geographic", + "name": "debugging_text_bd", "kind": "function", - "file": "src/underworld3/coordinates.py", - "line": 430, - "signature": "(x, y, z, a, b, max_iterations = 10, tolerance = 1e-12)", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 512, + "signature": "(randstr, fn, fn_type, eqn_no)", "parameters": [ { - "name": "x", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "y", + "name": "randstr", "type_hint": null, "default": null, "description": "" }, { - "name": "z", + "name": "fn", "type_hint": null, "default": null, "description": "" }, { - "name": "a", + "name": "fn_type", "type_hint": null, "default": null, "description": "" }, { - "name": "b", + "name": "eqn_no", "type_hint": null, "default": null, "description": "" - }, - { - "name": "max_iterations", - "type_hint": null, - "default": "10", - "description": "" - }, - { - "name": "tolerance", - "type_hint": null, - "default": "1e-12", - "description": "" } ], "returns": null, - "existing_docstring": "Convert Cartesian coordinates to geographic coordinates.\n\nUses iterative algorithm (Bowring's method) for geodetic latitude.\n\nParameters\n----------\nx, y, z : float or array\n Cartesian coordinates in km\na : float\n Semi-major axis (equatorial radius) in km\nb : float\n Semi-minor axis (polar radius) in km\nmax_iterations : int, optional\n Maximum iterations for latitude convergence (default: 10)\ntolerance : float, optional\n Convergence tolerance in radians (default: 1e-12)\n\nReturns\n-------\nlon_deg, lat_deg, depth_km : float or array\n Geographic coordinates:\n - lon_deg: Longitude in degrees East\n - lat_deg: Latitude in degrees North (geodetic)\n - depth_km: Depth below ellipsoid surface in km", - "harvested_comments": [ - "Longitude is straightforward", - "Latitude requires iteration (Bowring's method for geodetic latitude)", - "Initial guess for latitude (geocentric)", - "Iterate to converge on geodetic latitude", - "Check convergence" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "lon", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 585, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Longitude in degrees East (-180 to 180).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": null, "is_public": true }, { - "name": "lat", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 591, + "name": "start", + "kind": "method", + "file": "src/underworld3/utilities/_utils.py", + "line": 164, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Geodetic latitude in degrees North (-90 to 90).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": true - }, - { - "name": "depth", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 597, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Depth below ellipsoid surface (positive downward).\n\nReturns nondimensional values when units are active, km otherwise.\nUse with units system for proper dimensional output.", - "harvested_comments": [ - "Return with units if available", - "Check if units system is active for wrapping", - "Get reference length to dimensionalize", - "Depth in km = nondimensional * L_ref" - ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "CaptureStdout", "is_public": true }, { - "name": "coords", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 624, + "name": "stop", + "kind": "method", + "file": "src/underworld3/utilities/_utils.py", + "line": 168, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Geographic coordinates as (N, 3) array: [lon, lat, depth].\n\nReturns mesh node coordinates in geographic form, matching the\nlayout of `mesh.X.coords` but in (longitude, latitude, depth) format.\n\nReturns\n-------\nnumpy.ndarray\n Shape (N, 3) array where columns are:\n - Column 0: Longitude (degrees East, -180 to 180)\n - Column 1: Latitude (degrees North, -90 to 90)\n - Column 2: Depth (km below ellipsoid surface, positive down)\n\nExamples\n--------\n>>> geo_coords = mesh.CoordinateSystem.geo.coords\n>>> print(f\"Node 0: lon={geo_coords[0,0]:.2f}\u00b0, lat={geo_coords[0,1]:.2f}\u00b0\")\n\nSee Also\n--------\nmesh.X.coords : Cartesian coordinates (x, y, z)\nlon, lat, depth : Individual coordinate arrays", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "CaptureStdout", "is_public": true }, { - "name": "unit_WE", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 668, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "West to East unit vector (primary name, positive East).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "h5_scan", + "kind": "function", + "file": "src/underworld3/utilities/_utils.py", + "line": 174, + "signature": "(filename)", + "parameters": [ + { + "name": "filename", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": true - }, - { - "name": "unit_SN", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 673, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "South to North unit vector (primary name, positive North).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": null, "is_public": true }, { - "name": "unit_down", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 678, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Downward unit vector (primary name, positive into planet).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "install", + "kind": "method", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 596, + "signature": "(self, solver, verbose = False)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": true - }, - { - "name": "unit_east", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 685, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Eastward unit vector (directional alias for unit_WE).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "CustomMGHierarchy", "is_public": true }, { - "name": "unit_west", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 690, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Westward unit vector (opposite of unit_WE).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "points_in_simplex3D", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 46, + "signature": "(p, a, b, c, d)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "c", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "d", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": true - }, - { - "name": "unit_north", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 695, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Northward unit vector (directional alias for unit_SN).", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [ + "ScTP computes the scalar triple product a.(bxc):" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": null, "is_public": true }, { - "name": "unit_south", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 700, - "signature": "(self)", - "parameters": [], + "name": "read_ascii_buffer", + "kind": "function", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 109, + "signature": "(f, mesh_data_name, int_type = np.int32)", + "parameters": [ + { + "name": "f", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh_data_name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "int_type", + "type_hint": null, + "default": "np.int32", + "description": "" + } + ], "returns": null, - "existing_docstring": "Southward unit vector (opposite of unit_SN).", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [ + "e.g. Dimension\\n3, where the number of dimensions is on the next line", + "return vertices", + "return cells, triangles, edges", + "The first value is the number of elements", + "return corners, RequiredVertices" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": null, "is_public": true }, { - "name": "unit_up", + "name": "units", "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 705, + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 103, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Upward unit vector (opposite of unit_down).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "TimeSymbol", "is_public": true }, { - "name": "unit_lon", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 712, - "signature": "(self)", - "parameters": [], + "name": "units", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 107, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Longitude direction unit vector (coordinate alias for unit_WE).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "TimeSymbol", "is_public": true }, { - "name": "unit_lat", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 717, + "name": "get_units", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 110, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Latitude direction unit vector (coordinate alias for unit_SN).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "TimeSymbol", "is_public": true }, { - "name": "unit_depth", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 722, - "signature": "(self)", - "parameters": [], + "name": "writeHeader", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 48, + "signature": "(self, fp, hdfFilename)", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "hdfFilename", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Depth direction unit vector (coordinate alias for unit_down).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "to_cartesian", + "name": "writeCells", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 728, - "signature": "(self, lon, lat, depth)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 61, + "signature": "(self, fp, topologyPath, numCells, numCorners, cellsName = 'cells')", "parameters": [ { - "name": "lon", + "name": "fp", "type_hint": null, "default": null, "description": "" }, { - "name": "lat", + "name": "topologyPath", "type_hint": null, "default": null, "description": "" }, { - "name": "depth", + "name": "numCells", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numCorners", "type_hint": null, "default": null, "description": "" + }, + { + "name": "cellsName", + "type_hint": null, + "default": "'cells'", + "description": "" } ], "returns": null, - "existing_docstring": "Convert geographic coordinates to Cartesian (x, y, z).\n\nUses the mesh's ellipsoid parameters automatically.\nWhen units are active, depth should be nondimensional and returns\nnondimensional Cartesian coordinates.\n\nParameters\n----------\nlon : float or array_like\n Longitude in degrees East (-180 to 180)\nlat : float or array_like\n Geodetic latitude in degrees North (-90 to 90)\ndepth : float or array_like\n Depth below ellipsoid surface (nondimensional if units active, km otherwise)\n\nReturns\n-------\ntuple\n (x, y, z) coordinates (nondimensional if units active, km otherwise)\n\nExamples\n--------\n>>> # Import external data in geographic coordinates\n>>> tomo_lon = np.array([136.0, 136.5, 137.0])\n>>> tomo_lat = np.array([-34.0, -33.5, -33.0])\n>>> tomo_depth = np.array([10.0, 20.0, 30.0]) # km or nondimensional\n>>> x, y, z = mesh.geo.to_cartesian(tomo_lon, tomo_lat, tomo_depth)", - "harvested_comments": [ - "Import external data in geographic coordinates", - "km or nondimensional", - "Use nondimensional ellipsoid if available" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "from_cartesian", + "name": "writeVertices", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 768, - "signature": "(self, x, y, z)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 76, + "signature": "(self, fp, geometryPath, numVertices, spaceDim)", "parameters": [ { - "name": "x", + "name": "fp", "type_hint": null, "default": null, "description": "" }, { - "name": "y", + "name": "geometryPath", "type_hint": null, "default": null, "description": "" }, { - "name": "z", + "name": "numVertices", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert Cartesian coordinates to geographic (lon, lat, depth).\n\nUses the mesh's ellipsoid parameters automatically.\nWhen units are active, coordinates should be nondimensional.\n\nParameters\n----------\nx : float or array_like\n X coordinate (nondimensional if units active, km otherwise)\ny : float or array_like\n Y coordinate\nz : float or array_like\n Z coordinate\n\nReturns\n-------\ntuple\n (lon, lat, depth) where:\n - lon: Longitude in degrees East (-180 to 180)\n - lat: Geodetic latitude in degrees North (-90 to 90)\n - depth: Depth (nondimensional if units active, km otherwise)\n\nExamples\n--------\n>>> # Convert mesh points to geographic for comparison with data\n>>> x, y, z = mesh.data[:, 0], mesh.data[:, 1], mesh.data[:, 2]\n>>> lon, lat, depth = mesh.geo.from_cartesian(x, y, z)", - "harvested_comments": [ - "Convert mesh points to geographic for comparison with data", - "Use nondimensional ellipsoid if available" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "points_to_cartesian", + "name": "writeLocations", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 808, - "signature": "(self, points_geo)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 90, + "signature": "(self, fp, numParticles, spaceDim)", "parameters": [ { - "name": "points_geo", + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numParticles", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array of geographic points to Cartesian coordinates.\n\nConvenience method for importing external data.\n\nParameters\n----------\npoints_geo : array_like\n Array of shape (N, 3) with columns [lon, lat, depth]\n - lon: Longitude in degrees East\n - lat: Geodetic latitude in degrees North\n - depth: Depth in km below surface\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [x, y, z] in km\n\nExamples\n--------\n>>> # Import seismicity catalog\n>>> eq_llz = np.loadtxt(\"earthquakes.csv\", delimiter=\",\") # [lon, lat, depth]\n>>> eq_xyz = mesh.geo.points_to_cartesian(eq_llz)\n>>> # Now use with KDTree or swarm.add_particles_with_coordinates()", - "harvested_comments": [ - "Import seismicity catalog", - "[lon, lat, depth]", - "Now use with KDTree or swarm.add_particles_with_coordinates()" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "points_from_cartesian", + "name": "writeTimeGridHeader", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 840, - "signature": "(self, points_xyz)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 103, + "signature": "(self, fp, time)", "parameters": [ { - "name": "points_xyz", + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "time", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array of Cartesian points to geographic coordinates.\n\nParameters\n----------\npoints_xyz : array_like\n Array of shape (N, 3) with columns [x, y, z] in km\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [lon, lat, depth]\n\nExamples\n--------\n>>> # Export mesh coordinates to geographic\n>>> mesh_xyz = mesh.data # or mesh.CoordinateSystem.coords\n>>> mesh_llz = mesh.geo.points_from_cartesian(mesh_xyz)", - "harvested_comments": [ - "Export mesh coordinates to geographic", - "or mesh.CoordinateSystem.coords" - ], - "status": "complete", - "needs": [], - "parent_class": "GeographicCoordinateAccessor", - "is_public": true - }, - { - "name": "view", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 881, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Display a formatted summary of available properties and methods.\n\nThis method prints a helpful guide to the geographic coordinate system,\nshowing all available data arrays, symbolic coordinates, unit vectors,\nand conversion methods.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": true - }, - { - "name": "r", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1038, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Radial distance from origin.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "theta", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1044, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Colatitude in radians (0 at north pole, \u03c0 at south pole).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "writeHybridSpaceGridHeader", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 122, + "signature": "(self, fp)", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "phi", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1050, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Longitude/azimuth in radians (-\u03c0 to \u03c0). Only available for 3D meshes.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "coords", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1061, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Spherical/polar coordinates as array.\n\nReturns mesh node coordinates in spherical/polar form:\n- 3D: (N, 3) array [r, \u03b8, \u03c6]\n- 2D: (N, 2) array [r, \u03b8]\n\nReturns\n-------\nnumpy.ndarray\n For 3D (spherical): Shape (N, 3) array where columns are:\n\n - Column 0: Radius (same units as mesh)\n - Column 1: Colatitude \u03b8 in radians (0 at north pole)\n - Column 2: Longitude \u03c6 in radians (-\u03c0 to \u03c0)\n\n For 2D (polar): Shape (N, 2) array where columns are:\n\n - Column 0: Radius (same units as mesh)\n - Column 1: Polar angle \u03b8 in radians\n\nExamples\n--------\n>>> sph_coords = mesh.X.spherical.coords\n>>> print(f\"Node 0: r={sph_coords[0,0]:.3f}, \u03b8={np.degrees(sph_coords[0,1]):.1f}\u00b0\")\n\nSee Also\n--------\nmesh.X.coords : Cartesian coordinates (x, y) or (x, y, z)\nr, theta, phi : Individual coordinate arrays", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "name": "writeSpaceGridHeader", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 126, + "signature": "(self, fp, numCells, numCorners, cellDim, spaceDim, cellsName = 'cells')", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numCells", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numCorners", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cellDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cellsName", + "type_hint": null, + "default": "'cells'", + "description": "" + } ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "r_sym", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1121, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Symbolic radial coordinate.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "theta_sym", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1126, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Symbolic polar/colatitude coordinate.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "writeFieldSingle", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 152, + "signature": "(self, fp, numSteps, timestep, spaceDim, name, f, domain)", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numSteps", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "timestep", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "f", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "domain", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "phi_sym", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1131, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Symbolic longitude coordinate (3D only).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": null, + "harvested_comments": [ + "self.typeMap[f[1].attrs[\"vector_field_type\"]]," ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "unit_r", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1143, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Radial unit vector (outward from origin).", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "unit_theta", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1148, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Tangential unit vector (direction of increasing \u03b8).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "writeFieldComponents", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 214, + "signature": "(self, fp, numSteps, timestep, spaceDim, name, f, domain)", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numSteps", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "timestep", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "f", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "domain", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "unit_phi", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1153, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Azimuthal unit vector (eastward, direction of increasing \u03c6). 3D only.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": null, + "harvested_comments": [ + "vtype = f[1].attrs[\"vector_field_type\"]", + "f\"name = {name}, dof = {dof}, dims = {dims}, cdims = {cdims}, bs = {bs}, spaceDim = {spaceDim}\",", + "flush=True,", + "if bs == spaceDim:", + "vtype = b\"vector\"" ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "unit_radial", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1165, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Alias for unit_r.", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "unit_outward", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1170, - "signature": "(self)", - "parameters": [], + "name": "writeField", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 290, + "signature": "(self, fp, numSteps, timestep, cellDim, spaceDim, name, f, domain)", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numSteps", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "timestep", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cellDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "f", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "domain", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Outward radial unit vector (alias for unit_r).", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [ + "print(f\"Writing field {f}\")", + "print(f\"cellDim: {cellDim}\")", + "print(f\"spaceDim: {spaceDim}\")", + "print(f\"Number of Cells {self.numCells}\")", + "print(f\"Number of Verticds {self.numVertices}\")" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "unit_inward", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1175, - "signature": "(self)", - "parameters": [], + "name": "writeSpaceGridFooter", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 323, + "signature": "(self, fp)", + "parameters": [ + { + "name": "fp", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Inward radial unit vector (opposite of unit_r).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "to_cartesian", + "name": "writeParticleGridHeader", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1181, - "signature": "(self, r, theta, phi)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 327, + "signature": "(self, fp, numParticles, spaceDim)", "parameters": [ { - "name": "r", + "name": "fp", "type_hint": null, "default": null, "description": "" }, { - "name": "theta", + "name": "numParticles", "type_hint": null, "default": null, "description": "" }, { - "name": "phi", + "name": "spaceDim", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert spherical coordinates to Cartesian (x, y, z).\n\nParameters\n----------\nr : float or array_like\n Radial distance\ntheta : float or array_like\n Colatitude in radians (0 at north pole)\nphi : float or array_like\n Longitude in radians\n\nReturns\n-------\ntuple\n (x, y, z) Cartesian coordinates", + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SphericalCoordinateAccessor", + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Xdmf", "is_public": true }, { - "name": "from_cartesian", + "name": "writeParticleField", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1209, - "signature": "(self, x, y, z)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 340, + "signature": "(self, fp, fieldname, numParticles, numComp)", "parameters": [ { - "name": "x", + "name": "fp", "type_hint": null, "default": null, "description": "" }, { - "name": "y", + "name": "fieldname", "type_hint": null, "default": null, "description": "" }, { - "name": "z", + "name": "numParticles", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numComp", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert Cartesian coordinates to spherical (r, \u03b8, \u03c6).\n\nParameters\n----------\nx : float or array_like\n X coordinate\ny : float or array_like\n Y coordinate\nz : float or array_like\n Z coordinate\n\nReturns\n-------\ntuple\n (r, theta, phi) where:\n\n - r: Radial distance\n - theta: Colatitude in radians (0 at north pole)\n - phi: Longitude in radians (-\u03c0 to \u03c0)", + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SphericalCoordinateAccessor", + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Xdmf", "is_public": true }, { - "name": "points_to_cartesian", + "name": "writeTimeGridFooter", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1241, - "signature": "(self, points_sph)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 355, + "signature": "(self, fp)", "parameters": [ { - "name": "points_sph", + "name": "fp", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array of spherical points to Cartesian coordinates.\n\nParameters\n----------\npoints_sph : array_like\n Array of shape (N, 3) with columns [r, theta, phi]\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [x, y, z]", + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SphericalCoordinateAccessor", + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Xdmf", "is_public": true }, { - "name": "points_from_cartesian", + "name": "writeFooter", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1261, - "signature": "(self, points_xyz)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 359, + "signature": "(self, fp)", "parameters": [ { - "name": "points_xyz", + "name": "fp", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array of Cartesian points to spherical coordinates.\n\nParameters\n----------\npoints_xyz : array_like\n Array of shape (N, 3) with columns [x, y, z]\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [r, theta, phi]", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SphericalCoordinateAccessor", - "is_public": true - }, - { - "name": "view", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1292, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Display a formatted summary of available properties and methods.\n\nThis method prints a helpful guide to the spherical coordinate system,\nshowing all available data arrays, symbolic coordinates, unit vectors,\nand conversion methods.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "Xdmf", "is_public": true }, { - "name": "coords", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1874, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Coordinate data array in physical units.\n\nReturns the mesh node coordinates, applying scaling if the mesh has\nreference quantities set. When mesh.units is specified, returns a\nUnitAwareArray.\n\nReturns:\n numpy.ndarray or UnitAwareArray: Node coordinates", - "harvested_comments": [ - "Apply scaling to convert model coordinates to physical coordinates", - "Wrap with unit-aware array if units are specified", - "Coordinates are scaled to SI base units (meters), not the reference unit", - "The scale factor (self._length_scale) converts dimensionless (0-1) to meters", - "So we label the result as \"meter\" regardless of the original reference unit" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "units", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1906, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Coordinate units.\n\nReturns the units for the coordinate system. This is the same as mesh.units\nand indicates what physical units the coordinates are expressed in.\n\nReturns:\n str or None: Coordinate units (e.g., 'km', 'm', 'degrees')", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "name": "write", + "kind": "method", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 363, + "signature": "(self, hdfFilename, topologyPath, numCells, numCorners, cellDim, htopologyPath, numHCells, numHCorners, geometryPath, numVertices, spaceDim, time, vfields, cfields, numParticles, pfields)", + "parameters": [ + { + "name": "hdfFilename", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "topologyPath", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numCells", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numCorners", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cellDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "htopologyPath", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numHCells", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numHCorners", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "geometryPath", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numVertices", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "spaceDim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "time", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vfields", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cfields", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numParticles", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pfields", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "with_units", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1919, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Coordinate symbols with unit information.\n\nDEPRECATED (2025-11-26): Following the Transparent Container Principle,\nthis now returns raw coordinate symbols. Units are derived on demand via\nuw.get_units() which finds the _units attribute on coordinate atoms.\n\nExamples:\n >>> x, y = mesh.X.with_units # Same as mesh.X[0], mesh.X[1]\n >>> area = x * y # Raw SymPy Mul; uw.get_units(area) \u2192 km**2\n >>> uw.get_units(x) # \u2192 kilometer (derived from _units attribute)\n\nReturns:\n tuple: Coordinate symbols (x, y) or (x, y, z)", + "existing_docstring": null, "harvested_comments": [ - "Same as mesh.X[0], mesh.X[1]", - "Raw SymPy Mul; uw.get_units(area) \u2192 km**2", - "\u2192 kilometer (derived from _units attribute)", - "TRANSPARENT CONTAINER PRINCIPLE (2025-11-26):", - "Just return raw coordinates. They have _units attributes from patching," + "Field information" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "Xdmf", "is_public": true }, { - "name": "shape", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1941, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Shape of the symbolic coordinate matrix.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "generateXdmf", + "kind": "function", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 481, + "signature": "(hdfFilename, xdmfFilename = None)", + "parameters": [ + { + "name": "hdfFilename", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "xdmfFilename", + "type_hint": null, + "default": "None", + "description": "" + } ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "is_symbol", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1967, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "SymPy type check - CoordinateSystem contains symbols but is not itself a symbol.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "is_Matrix", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1972, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "SymPy type check - CoordinateSystem behaves like a Matrix.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "generate_uw_Xdmf", + "kind": "function", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 556, + "signature": "(hdfFilename, xdmfFilename = None)", + "parameters": [ + { + "name": "hdfFilename", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "xdmfFilename", + "type_hint": null, + "default": "None", + "description": "" + } ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "is_scalar", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1977, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "SymPy type check - CoordinateSystem is a Matrix, not a scalar.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "is_number", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1982, - "signature": "(self)", - "parameters": [], + "name": "use_nondimensional_scaling", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 342, + "signature": "(enabled = True)", + "parameters": [ + { + "name": "enabled", + "type_hint": null, + "default": "True", + "description": "" + } + ], "returns": null, - "existing_docstring": "SymPy type check - CoordinateSystem is not a number.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Enable or disable non-dimensional scaling globally.\n\nWhen enabled, equations are scaled during unwrap() for better numerical\nconditioning. Variables with units and reference scales are automatically\nscaled (divided by their scaling_coefficient).\n\nThis is the ONLY way to control non-dimensional scaling - there are no\nother flags, solver settings, or context managers that affect this.\n\nParameters\n----------\nenabled : bool, default=True\n True to enable non-dimensional scaling, False to disable\n\nNotes\n-----\n- Scaling coefficients are ALWAYS computed from model.set_reference_quantities()\n- This flag only controls whether those coefficients are APPLIED during unwrap()\n- Changing this flag requires recompiling solvers (set solver.is_setup=False)\n\nExamples\n--------\nSetup problem with reference quantities:\n\n>>> model = uw.Model()\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(100, \"km\"),\n... temperature_diff=uw.quantity(1000, \"kelvin\")\n... )\n>>> T = uw.discretisation.MeshVariable('T', mesh, 1, units='kelvin')\n\nSolve with dimensional form (default):\n\n>>> uw.use_nondimensional_scaling(False) # Default\n>>> poisson.solve()\n>>> u_dimensional = T.array.copy()\n\nSolve with non-dimensional form (for comparison):\n\n>>> uw.use_nondimensional_scaling(True)\n>>> poisson.is_setup = False # Force recompilation with new scaling\n>>> poisson.solve()\n>>> u_nondimensional = T.array.copy()\n>>>\n>>> # Solutions should be identical\n>>> import numpy as np\n>>> assert np.allclose(u_dimensional, u_nondimensional)\n\nSee Also\n--------\nis_nondimensional_scaling_active : Check current scaling state\nmodel.set_reference_quantities : Set reference scales", + "harvested_comments": [ + "Force recompilation with new scaling", + "Solutions should be identical" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "is_commutative", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 1987, - "signature": "(self)", + "name": "is_nondimensional_scaling_active", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 401, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "SymPy type check - delegate to underlying matrix.", + "existing_docstring": "Check if non-dimensional scaling is currently enabled.\n\nReturns\n-------\nbool\n True if non-dimensional scaling is active, False otherwise\n\nSee Also\n--------\nuse_nondimensional_scaling : Enable/disable scaling", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "X", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2067, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Cartesian coordinates as UWCoordinate objects (user-facing symbolic).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "nondimensional_scaling_context", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 418, + "signature": "(enabled = True)", + "parameters": [ + { + "name": "enabled", + "type_hint": null, + "default": "True", + "description": "" + } ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "x", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2072, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Alias for natural coordinates (lowercase convention).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "returns": null, + "existing_docstring": "Context manager to temporarily set non-dimensional scaling state.\n\nAutomatically restores the previous state when exiting the context.\nThis is useful for operations that need to temporarily work in\nnon-dimensional space (like semi-Lagrangian advection) without\naffecting the global state.\n\nParameters\n----------\nenabled : bool, default=True\n True to enable non-dimensional scaling within context\n\nExamples\n--------\n>>> # Temporarily enable non-dimensional mode\n>>> with uw.nondimensional_scaling_context(True):\n... # evaluate() returns non-dimensional results here\n... velocity_nd = uw.function.evaluate(u.sym, coords)\n>>> # Original state restored automatically\n\nSee Also\n--------\nuse_nondimensional_scaling : Permanently set scaling state\nis_nondimensional_scaling_active : Check current state", + "harvested_comments": [ + "Temporarily enable non-dimensional mode", + "evaluate() returns non-dimensional results here", + "Original state restored automatically" ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "N", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2077, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Raw BaseScalar coordinates for derivatives and JIT compilation.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "use_strict_units", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 466, + "signature": "(enabled = True)", + "parameters": [ + { + "name": "enabled", + "type_hint": null, + "default": "True", + "description": "" + } ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "R", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2082, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Natural (curvilinear) coordinates :math:`(r, \\theta, \\phi)` or geographic.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "returns": null, + "existing_docstring": "Enable or disable strict units enforcement.\n\n**DEFAULT: ON** - Strict units mode is enabled by default to enforce best\npractices from the start. Variables with units REQUIRE reference quantities.\n\nWhen disabled, variables with units are allowed without reference quantities\nbut will get scaling_coefficient=1.0 and a warning. This \"half-way zone\"\nleads to poor numerical conditioning and silent errors.\n\n**Disabling strict mode is only recommended for:**\n- Expert users who understand the implications\n- Debugging specific issues\n- Legacy code migration (temporary)\n\nParameters\n----------\nenabled : bool, default=True\n True to enforce strict units (DEFAULT - recommended)\n False to allow units without reference quantities (expert/debugging only)\n\nExamples\n--------\nNormal usage (strict mode ON by default):\n\n>>> import underworld3 as uw\n>>> # Strict mode is ON by default - no need to enable\n>>>\n>>> # Set reference quantities FIRST:\n>>> model = uw.get_default_model()\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(1000, 'km'),\n... plate_velocity=uw.quantity(5, 'cm/year')\n... )\n>>>\n>>> # Then create mesh and variables:\n>>> mesh = uw.meshing.StructuredQuadBox(elementRes=(4, 4))\n>>> v = uw.discretisation.MeshVariable(\"v\", mesh, 2, units=\"m/s\") # \u2713 OK\n\nDisable for debugging (expert use only):\n\n>>> uw.use_strict_units(False) # Expert/debugging only\n>>> mesh = uw.meshing.StructuredQuadBox(elementRes=(4, 4))\n>>> v = uw.discretisation.MeshVariable(\"v\", mesh, 2, units=\"m/s\") # \u26a0\ufe0f Warning\n\nSee Also\n--------\nis_strict_units_active : Check current strict mode\nModel.set_reference_quantities : Set reference quantities", + "harvested_comments": [ + "Strict mode is ON by default - no need to enable", + "Set reference quantities FIRST:", + "Then create mesh and variables:", + "Expert/debugging only" ], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "r", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2087, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Symbolic natural coordinate variables (for mathematical expressions).", - "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "xR", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2092, - "signature": "(self) -> sympy.Matrix", + "name": "is_strict_units_active", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 521, + "signature": "()", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Alias for ``R`` (backward compatibility).", + "returns": null, + "existing_docstring": "Check if strict units enforcement is enabled.\n\nReturns\n-------\nbool\n True if strict units mode is active, False otherwise\n\nSee Also\n--------\nuse_strict_units : Enable/disable strict units", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "geo", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2097, - "signature": "(self)", - "parameters": [], + "name": "unwrap", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 567, + "signature": "(fn, keep_constants = True, return_self = True, apply_scaling = False)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "keep_constants", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "return_self", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "apply_scaling", + "type_hint": null, + "default": "False", + "description": "" + } + ], "returns": null, - "existing_docstring": "Geographic coordinates for GEOGRAPHIC meshes.\n\nProvides access to longitude, latitude, depth coordinates\nand geographic basis vectors on ellipsoidal (WGS84) meshes.\n\nReturns\n-------\nGeographicCoordinateAccessor\n Object with .lon, .lat, .depth, .coords, unit vectors, etc.\n Use .view() for a complete summary of available properties.\n\nRaises\n------\nAttributeError\n If coordinate system is not GEOGRAPHIC. The error message\n indicates what coordinate system IS available for this mesh.\n\nExamples\n--------\n>>> lon = mesh.X.geo.lon # Longitude data array\n>>> geo_coords = mesh.X.geo.coords # (N, 3) array [lon, lat, depth]\n>>> mesh.X.geo.view() # Show all available properties", + "existing_docstring": "Unwrap expressions with optional automatic scaling.\n\nParameters:\n-----------\nfn : expression\n The expression to unwrap\nkeep_constants : bool, default=True\n Whether to keep constants in the unwrapped expression\nreturn_self : bool, default=True\n Whether to return self if no unwrapping is needed\napply_scaling : bool, default=False\n Whether to automatically apply scale factors to variables with units\n\nExample:\n model = uw.Model()\n model.set_reference_quantities(mantle_temperature=1500*uw.scaling.units.K)\n\n temperature = uw.discretisation.MeshVariable(\"T\", mesh, 1, units=\"K\")\n expr = uw.function.expression(\"heat\", 2 * temperature.sym, \"heat equation\")\n\n # Normal unwrap\n result = uw.unwrap(expr)\n\n # Unwrap with automatic scaling\n scaled_result = uw.unwrap(expr, apply_scaling=True)", "harvested_comments": [ - "Longitude data array", - "(N, 3) array [lon, lat, depth]", - "Show all available properties", - "Provide helpful error message indicating what IS available" + "Normal unwrap", + "Unwrap with automatic scaling" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "spherical", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2148, - "signature": "(self)", - "parameters": [], + "name": "synchronised_array_update", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 616, + "signature": "(context_info = 'user operations')", + "parameters": [ + { + "name": "context_info", + "type_hint": null, + "default": "'user operations'", + "description": "" + } + ], "returns": null, - "existing_docstring": "Spherical/polar coordinates for SPHERICAL and CYLINDRICAL2D meshes.\n\nProvides access to radius and angle coordinates, plus basis vectors:\n- 3D (SPHERICAL): r, \u03b8 (colatitude), \u03c6 (longitude)\n- 2D (CYLINDRICAL2D/Annulus): r, \u03b8 (polar angle)\n\nReturns\n-------\nSphericalCoordinateAccessor\n Object with .r, .theta, .coords, unit vectors, etc.\n For 3D also .phi. Use .view() for a complete summary.\n\nRaises\n------\nAttributeError\n If coordinate system is not SPHERICAL or CYLINDRICAL2D.\n The error message indicates what IS available for this mesh.\n\nExamples\n--------\n>>> r = mesh.X.spherical.r # Radius data array\n>>> sph_coords = mesh.X.spherical.coords # (N, 2) or (N, 3) array\n>>> mesh.X.spherical.view() # Show all available properties", + "existing_docstring": "Context manager for synchronised array updates across multiple variables.\n\nBatches multiple array assignments together and defers PETSc synchronization\nuntil the end of the context, ensuring atomic updates and better performance.\n\nExample\n-------\nwith uw.synchronised_array_update():\n velocity.array[...] = new_velocity_values\n pressure.array[...] = new_pressure_values\n temperature.array[...] = new_temperature_values\n# All arrays are synchronized here\n\nParameters\n----------\ncontext_info : str\n Optional description of the update context for debugging\n\nReturns\n-------\nContext manager for delayed callback execution", "harvested_comments": [ - "Radius data array", - "(N, 2) or (N, 3) array", - "Show all available properties", - "Provide helpful error message indicating what IS available" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "All arrays are synchronized here" ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "rRotN", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2200, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Rotation matrix from Cartesian (N) to natural (R) coordinates.\n\nTransforms vectors from Cartesian basis to the local curvilinear basis.\nFor spherical: rows are :math:`(\\hat{r}, \\hat{\\theta}, \\hat{\\phi})`.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "name": "create_metric", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 88, + "signature": "(mesh: 'Mesh', h_values: np.ndarray, name: str = None) -> 'MeshVariable'", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "h_values", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } ], - "parent_class": "CoordinateSystem", + "returns": "'MeshVariable'", + "existing_docstring": "Create adaptation metric from target edge lengths.\n\nThis is the core utility that converts target edge lengths (h-field) to\nthe metric tensor format required by MMG/PETSc mesh adaptation.\n\nParameters\n----------\nmesh : Mesh\n The mesh to create the metric on.\nh_values : np.ndarray\n Array of target edge lengths at each mesh node. Shape should be\n (n_nodes,) or (n_nodes, 1).\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"adaptation_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing metric values ready for mesh.adapt().\n\nNotes\n-----\n**Metric Tensor Mathematics**\n\nFor isotropic mesh adaptation, MMG/PETSc uses a metric tensor:\n\n.. math::\n\n M = h^{-2} \\cdot I\n\nwhere :math:`h` is the target edge length and :math:`I` is the identity\nmatrix. This relationship is **dimension-independent** - the same formula\napplies in 2D and 3D.\n\nHigher metric values produce smaller elements. The adaptation algorithm\nseeks to make :math:`\\mathbf{e}^T M \\mathbf{e} = 1` for all edges.\n\nExamples\n--------\n>>> # Create metric from h-field computed elsewhere\n>>> h_field = compute_error_based_h(solution) # User function\n>>> metric = uw.adaptivity.create_metric(mesh, h_field)\n>>> mesh.adapt(metric)\n\nSee Also\n--------\nmetric_from_gradient : Create metric from scalar field gradient.\nmetric_from_field : Create metric from indicator field.", + "harvested_comments": [ + "Create metric from h-field computed elsewhere", + "User function", + "Ensure h_values is the right shape", + "Create metric MeshVariable", + "Convert to metric tensor: M = 1/h\u00b2 \u00d7 I (isotropic)" + ], + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "xRotN", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2209, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Rotation matrix from Cartesian (N) to Cartesian (X) - typically identity.", - "harvested_comments": [], + "name": "metric_from_gradient", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 159, + "signature": "(field: 'MeshVariable', h_min: float, h_max: float, gradient_min: float = None, gradient_max: float = None, profile: str = 'linear', name: str = None) -> 'MeshVariable'", + "parameters": [ + { + "name": "field", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "h_min", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "h_max", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "gradient_min", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "gradient_max", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "profile", + "type_hint": "str", + "default": "'linear'", + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Create adaptation metric from gradient of a scalar field.\n\nProduces a metric that refines where gradients are steep (high $\\lvert\\nabla\\phi\\rvert$)\nand coarsens where the field is smooth (low $\\lvert\\nabla\\phi\\rvert$). This is the standard\napproach for error-driven or feature-based mesh adaptation.\n\nParameters\n----------\nfield : MeshVariable\n Scalar MeshVariable whose gradient drives refinement. Must have\n num_components=1.\nh_min : float\n Target edge length where gradient is highest (finest mesh).\nh_max : float\n Target edge length where gradient is lowest (coarsest mesh).\ngradient_min : float, optional\n Gradient magnitude below this uses h_max. If None, uses 5th percentile\n of gradient magnitude values.\ngradient_max : float, optional\n Gradient magnitude above this uses h_min. If None, uses 95th percentile\n of gradient magnitude values.\nprofile : str, optional\n Interpolation profile: \"linear\", \"smoothstep\", or \"power\" (default: \"linear\").\n\n - \"linear\": h varies linearly with gradient magnitude\n - \"smoothstep\": smooth S-curve transition ($C^1$ continuous)\n - \"power\": $h \\propto \\lvert\\nabla\\phi\\rvert^{-1/2}$, natural for error equidistribution\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"{field.name}_gradient_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing metric values ready for mesh.adapt().\n\nNotes\n-----\n**Gradient-Based Refinement Strategy**\n\nThe idea is that steep gradients indicate regions where the solution is\nchanging rapidly - these need finer resolution to capture accurately.\nSmooth regions can use coarser mesh without losing accuracy.\n\nThe mapping is:\n\n- High $\\lvert\\nabla\\phi\\rvert$ : small h : large metric : finer mesh\n- Low $\\lvert\\nabla\\phi\\rvert$ : large h : small metric : coarser mesh\n\n**Choosing h_min and h_max**\n\n- ``h_min`` controls finest resolution (where gradients are steepest)\n- ``h_max`` controls coarsest resolution (smooth regions)\n- Ratio ``h_max/h_min`` gives refinement factor (e.g., 10 = 10\u00d7 finer at peaks)\n\n**Auto-detection of Gradient Range**\n\nIf ``gradient_min`` and ``gradient_max`` are not specified, they are\ncomputed from the actual gradient field:\n\n- gradient_min = 5th percentile of $\\lvert\\nabla\\phi\\rvert$\n- gradient_max = 95th percentile of $\\lvert\\nabla\\phi\\rvert$\n\nThis ensures robust behavior even when gradient magnitudes span many\norders of magnitude.\n\n**Implementation Note**\n\nGradients are computed using the Clement interpolant via\n``uw.function.evaluate(field.sym.diff(x), coords)``. This uses PETSc's\n``DMPlexComputeGradientClementInterpolant`` which averages cell-wise\ngradients at vertices. The result is O(h) accurate and fast (no linear\nsolve required).\n\nExamples\n--------\n>>> # Refine based on temperature gradient\n>>> metric = uw.adaptivity.metric_from_gradient(\n... T, h_min=0.005, h_max=0.05, profile=\"smoothstep\"\n... )\n>>> mesh.adapt(metric)\n\n>>> # Refine based on strain rate\n>>> # First compute strain rate magnitude as scalar field\n>>> SR = uw.discretisation.MeshVariable(\"SR\", mesh, 1)\n>>> # ... populate SR with strain rate second invariant ...\n>>> metric = uw.adaptivity.metric_from_gradient(SR, h_min=0.01, h_max=0.1)\n>>> mesh.adapt(metric)\n\nSee Also\n--------\ncreate_metric : Create metric from h-field directly.\nmetric_from_field : Create metric from indicator field (not gradient).", + "harvested_comments": [ + "Refine based on temperature gradient", + "Refine based on strain rate", + "First compute strain rate magnitude as scalar field", + "... populate SR with strain rate second invariant ...", + "Compute gradient at mesh nodes using Clement interpolant" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "metric_from_field", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 322, + "signature": "(indicator: 'MeshVariable', h_min: float, h_max: float, indicator_min: float = None, indicator_max: float = None, invert: bool = False, profile: str = 'linear', name: str = None) -> 'MeshVariable'", + "parameters": [ + { + "name": "indicator", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "h_min", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "h_max", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "indicator_min", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "indicator_max", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "invert", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "profile", + "type_hint": "str", + "default": "'linear'", + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Create adaptation metric from an indicator field.\n\nMaps a scalar indicator field (e.g., error estimate, phase field, distance)\nto target edge lengths. This is more general than gradient-based adaptation -\nyou provide any field indicating where refinement is needed.\n\nParameters\n----------\nindicator : MeshVariable\n Scalar field indicating where refinement is needed. Higher values\n (by default) produce finer mesh.\nh_min : float\n Target edge length where indicator is highest (finest mesh).\nh_max : float\n Target edge length where indicator is lowest (coarsest mesh).\nindicator_min : float, optional\n Indicator values below this use h_max. If None, uses field minimum.\nindicator_max : float, optional\n Indicator values above this use h_min. If None, uses field maximum.\ninvert : bool, optional\n If True, high indicator values give coarse mesh (swap h_min/h_max roles).\n Useful when indicator represents \"smoothness\" rather than \"need for\n refinement\". Default: False.\nprofile : str, optional\n Interpolation profile: \"linear\" or \"smoothstep\". Default: \"linear\".\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"{indicator.name}_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing metric values ready for mesh.adapt().\n\nNotes\n-----\n**Use Cases**\n\n- **Error estimates**: Pass a computed error field; high error gives fine mesh\n- **Phase fields**: Refine at interfaces (field near transition value)\n- **Distance fields**: Refine near surfaces (use with Surface.distance)\n- **Material boundaries**: Refine near composition gradients\n\n**Relationship to Surface.refinement_metric()**\n\nThis function is a general-purpose version. Surface.refinement_metric()\nis a specialized wrapper that computes the indicator from surface distance.\n\nExamples\n--------\n>>> # Refine based on error estimate\n>>> error = compute_error_estimate(solution) # User function\n>>> metric = uw.adaptivity.metric_from_field(error, h_min=0.005, h_max=0.05)\n>>> mesh.adapt(metric)\n\n>>> # Refine at phase boundaries (phi transitions from 0 to 1)\n>>> # Want fine mesh where phi is near 0.5\n>>> phi_interface = 1 - 4 * (phi - 0.5)**2 # Peak at phi=0.5\n>>> metric = uw.adaptivity.metric_from_field(phi_interface, h_min=0.01, h_max=0.1)\n\nSee Also\n--------\ncreate_metric : Create metric from h-field directly.\nmetric_from_gradient : Create metric from field gradient.", + "harvested_comments": [ + "Refine based on error estimate", + "User function", + "Refine at phase boundaries (phi transitions from 0 to 1)", + "Want fine mesh where phi is near 0.5", + "Peak at phi=0.5" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "mesh2mesh_swarm", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 629, + "signature": "(mesh0, mesh1, swarm0, swarmVarList, proxy = True, verbose = False)", + "parameters": [ + { + "name": "mesh0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh1", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "swarm0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "swarmVarList", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "proxy", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Warning [NSFW] - this uses EXPLICIT message passing calls to handle the\nsituation where a swarm cell_dm cannot find particles after mesh redistribution.\nThis occurs when particles are moved accross non-neighbouring processes or if the\nmesh neighbours are redistricted. This should be fixed at the DMSwarm / DMPlex level\nso this code is just a placeholder. Or maybe it's just user error !\n\nNotes 1: This copies a swarm from one mesh to another allowing for completely incommensurate\npartitionings. Warning: this is not always a 1->1 mapping. There may be some duplication and\nparticles may go missing along curved boundaries where the meshes do not necessarily overlap.\nThe same is true in the shadow spaces.\n\nNote 2: The swarm is \"adapted\" to the original mesh and will need\nto be repopulated on the new one, or data can be mapped to a purpose-built swarm.\n\nNote 3: We pass the data around as floats for the time being. Be careful when converting back.\n\nNote 4: set proxy=True to automatically generate proxy variables on mesh1 but consider skipping\nif the returned swarm is ephemeral", + "harvested_comments": [ + "f\"{uw.mpi.rank} - A/local found: {n_found} v. not found: {n_not_found}\",", + "flush=False,", + "Let's sync the number of missing points by rank", + "print(f\"rank: {uw.mpi.rank}, local_array size: {n_not_found}\")", + "f\"{uw.mpi.rank} Sizes are: {global_sizes} Buffer size: {global_size}\"," + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "mesh2mesh_meshVariable", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 870, + "signature": "(meshVar0, meshVar1, verbose = False)", + "parameters": [ + { + "name": "meshVar0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "meshVar1", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Map a meshVar on mesh0 to a meshVar on mesh1 using\nan intermediary (temporary) swarm", + "harvested_comments": [ + "1 Create a temporary swarm with a variable that matches meshVar0", + "Maybe 3+ if var is higher order ??", + "Set data on the swarmVar", + "print(f\"Map data to swarm (rbf) - points = {tmp_swarm.dm.getSize()}\", flush=True)", + "print(f\"Distribute swarm\", flush=True)" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "geoRotN", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2214, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Ellipsoidal rotation matrix for GEOGRAPHIC coordinate systems.\n\nTransforms Cartesian vectors to the local geographic frame:\n- Row 0: geodetic up (perpendicular to ellipsoid surface)\n- Row 1: north (meridional, along ellipsoid surface)\n- Row 2: east (azimuthal, along ellipsoid surface)\n\nThis is the ellipsoidal equivalent of ``rRotN`` for spherical coordinates.\nFor a sphere (a=b), this reduces to the spherical rotation matrix.\n\nFor an ellipsoid, the geodetic normal differs from the radial direction\nby up to ~10 arcminutes at mid-latitudes, which is significant for\nregional models at scales of 10-100 km.\n\nReturns\n-------\nsympy.Matrix\n 3\u00d73 rotation matrix, or None if not GEOGRAPHIC coordinate system.\n\nExamples\n--------\nTransform a Cartesian velocity to geographic components:\n\n>>> v_cartesian = sympy.Matrix([[vx, vy, vz]])\n>>> v_geo = mesh.CoordinateSystem.geoRotN * v_cartesian.T\n>>> v_up, v_north, v_east = v_geo[0], v_geo[1], v_geo[2]", - "harvested_comments": [], + "name": "write_snapshot_skeleton", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 162, + "signature": "(model, path: str) -> str", + "parameters": [ + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Phase 1: write the metadata + empty skeleton group structure.\n\nReturns the path written. Subsequent phases (2: mesh + meshvar\nbulk; 3: swarms + python_state) populate the empty top-level\ngroups using PETSc primitives and dataclass serialisation\nrespectively. Writing is rank-0-only at this phase since no\ncollective PETSc operations are involved yet.", + "harvested_comments": [ + "Stub the other top-level groups so external readers can", + "see the file's intended shape from day one \u2014 phases 2/3", + "populate them.", + "set to \"phase2\" / \"phase3\" later" + ], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_e_0", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2248, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "First natural basis vector (radial :math:`\\hat{r}` for curvilinear).", - "harvested_comments": [], - "status": "minimal", + "name": "read_snapshot_metadata", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 199, + "signature": "(path: str) -> dict", + "parameters": [ + { + "name": "path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "dict", + "existing_docstring": "Read the ``/metadata`` group's attrs back as a plain dict.\n\nValidates the schema version. Lists stored as ``*_json`` are\ndecoded back into Python lists for caller convenience but the\non-disk form stays JSON for h5-tool friendliness.", + "harvested_comments": [ + "h5py returns bytes for some string attrs; normalise to str.", + "Decode JSON-encoded list fields for caller convenience.", + "e.g. \"mesh_names\" alongside \"mesh_names_json\"" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_e_1", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2253, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Second natural basis vector (:math:`\\hat{\\theta}` for curvilinear).", - "harvested_comments": [], - "status": "minimal", + "name": "write_snapshot", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 276, + "signature": "(model, path: str) -> str", + "parameters": [ + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Write a complete on-disk snapshot of the model's mesh + mesh-variable\nstate (phase 2 scope; swarms and python_state land in phase 3).\n\nProduces two artifacts:\n\n- ``path`` \u2014 the wrapper HDF5 file with rich metadata and the group\n structure inspectable via ``h5ls``.\n- ``_bulk_dir_for(path)`` \u2014 companion directory containing the\n PETSc HDF5 files (mesh DM + per-variable section/vec) produced\n by #146's :meth:`Mesh.write_checkpoint`.\n\nThe two are a unit; move them together. Returns the wrapper path.", + "harvested_comments": [ + "146's :meth:`Mesh.write_checkpoint`.", + "Phase-1 layer: metadata + skeleton groups.", + "rank-0 creates the bulk directory; collective ops below need it", + "to exist on the rank doing the PETSc-HDF5 write (which is rank 0", + "in this single-file write \u2014 actually PETSc's HDF5 viewer is" + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_e_2", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2258, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Third natural basis vector (:math:`\\hat{\\phi}` for spherical, None for 2D).", - "harvested_comments": [], - "status": "minimal", + "name": "read_snapshot", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 449, + "signature": "(model, path: str) -> None", + "parameters": [ + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Load mesh-variable DOFs from an on-disk snapshot into the model.\n\nThe model must already have the same meshes (by name) and the\nsame variables (by ``clean_name``) registered \u2014 this is the\nsame-rank-count restart path that mirrors :func:`restore` for the\nin-memory snapshot. Cross-run / rebuild-on-load is v1.2 scope.\n\nBulk data is read via #146's :meth:`MeshVariable.read_checkpoint`;\nno KDTree remapping (that's phase 4's compatibility layer in\n``read_timestep``).", + "harvested_comments": [ + "146's :meth:`MeshVariable.read_checkpoint`;", + "Build {original_name -> registered Mesh} for lookup", + "Phase 3a: restore state-bearer dataclasses.", + "Phase 3b + 6: restore swarms from per-rank sidecars.", + "Resolve the per-rank sidecar name from the pattern." + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_i", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2266, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Cartesian unit vector :math:`\\hat{i}` (x-direction).", + "name": "is_snapshot_wrapper", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 892, + "signature": "(path: str) -> bool", + "parameters": [ + { + "name": "path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Quick check whether ``path`` is a v1.1 snapshot wrapper file.\n\nUsed by :meth:`MeshVariable.read_timestep` to dispatch between\nthe legacy per-variable layout and the v1.1 sidecar format \u2014\nsame user call, different storage, hidden behind the function.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_j", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2271, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Cartesian unit vector :math:`\\hat{j}` (y-direction).", - "harvested_comments": [], - "status": "minimal", + "name": "extract_var_via_bridge", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 908, + "signature": "(wrapper_path: str, var_name: str)", + "parameters": [ + { + "name": "wrapper_path", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "var_name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Bridge for selective per-variable reads of v1.1 snapshots.\n\nGiven the wrapper path and a variable name, returns\n``(coords, values)`` numpy arrays \u2014 exactly what\n:meth:`MeshVariable.read_timestep` produces on rank 0 from the\nlegacy layout. The rest of read_timestep's swarm-routing +\nKDTree machinery is format-agnostic; this bridge is what makes\n``read_timestep`` work transparently against new files.\n\nMechanism: load the source mesh from the .mesh.h5 sidecar,\nrebuild the source variable with the correct shape, load DOFs\nvia #146's ``read_checkpoint``, then read out ``var.coords`` +\n``var.array``.", + "harvested_comments": [ + "146's ``read_checkpoint``, then read out ``var.coords`` +", + "Rebuild a transient source mesh + variable to read DOFs into.", + "We deliberately don't register them with the live model \u2014 these", + "are throwaway and exit scope on return." + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_k", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2276, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Cartesian unit vector :math:`\\hat{k}` (z-direction, None for 2D).", + "name": "inspect_snapshot", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 967, + "signature": "(path: str) -> str", + "parameters": [ + { + "name": "path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Human-readable one-shot summary of a snapshot file's metadata.\n\nUseful as a Python-side equivalent to running ``h5ls`` on the\nfile; intended for ``print(uw.checkpoint.inspect_snapshot(path))``\nat a notebook prompt.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_ijk", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2283, - "signature": "(self, dirn) -> sympy.Matrix", + "name": "snapshot", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 171, + "signature": "(model) -> Snapshot", "parameters": [ { - "name": "dirn", + "name": "model", "type_hint": null, "default": null, "description": "" } ], - "returns": "sympy.Matrix", - "existing_docstring": "Return Cartesian unit vector for direction index (0=i, 1=j, 2=k).", + "returns": "Snapshot", + "existing_docstring": "Capture a unitary snapshot of the model's current state.\n\nCaptures, in v1: each registered mesh's deformed coordinates and\nevery mesh-variable's global-vector DOFs; each registered swarm's\nper-rank particle coordinates and user swarm-variable arrays.\n\nPass ``path=...`` once the v1.1 on-disk backend lands. v1 raises\n``NotImplementedError``.\n\nSee ``docs/developer/design/in_memory_checkpoint_design.md`` for\nthe design rationale and scope boundaries.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_vertical", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2292, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Primary vertical direction for this coordinate system", - "harvested_comments": [ - "In Cartesian, vertical is the last coordinate direction", - "y-direction in 2D", - "z-direction in 3D", - "In cylindrical 2D, \"vertical\" is ambiguous but typically means Cartesian y", - "In spherical, \"vertical\" typically means radial outward" + "name": "restore", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 268, + "signature": "(model, snap: Snapshot) -> None", + "parameters": [ + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "snap", + "type_hint": "Snapshot", + "default": null, + "description": "" + } ], - "status": "minimal", + "returns": "None", + "existing_docstring": "Restore the model from a snapshot.\n\nMesh restore in v1 writes captured coords + DOFs back in place. If\nthe mesh's ``_mesh_version`` has moved since capture, restore\nraises :class:`SnapshotInvalidatedError` \u2014 this becomes a rebuild\npath in v1.2.\n\nSwarm restore *rebuilds* the local particle population: clears\ncurrent particles, re-adds at captured coords, writes captured\nper-variable data back in order. This is the rebuild-on-restore\nsemantics described in the design note's \"Restore semantics for\nswarms\" section \u2014 restore is precisely *for* the case where\nparticles have moved / been added / been removed since capture.\n\nParameters\n----------\nmodel\n The :class:`underworld3.Model` to restore. Must be the same\n instance the snapshot came from (within-process restore).\nsnap\n Token returned by :func:`snapshot`.\n\nRaises\n------\nSnapshotInvalidatedError\n Captured mesh / swarm / variable is no longer registered on\n the model, or mesh ``_mesh_version`` has moved since capture\n (mesh-adapt is v1.2 scope).\nTypeError\n ``snap`` is not a :class:`Snapshot`.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_horizontal", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2312, - "signature": "(self) -> sympy.Matrix", + "name": "keys", + "kind": "method", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 113, + "signature": "(self)", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Primary horizontal direction for this coordinate system", - "harvested_comments": [ - "x-direction", - "In cylindrical, horizontal could be radial or tangential - choose radial as primary", - "radial direction", - "In spherical, horizontal is typically tangential (theta direction)", - "meridional direction" - ], + "returns": null, + "existing_docstring": "Names of all managed quantities (including time/step/dt).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "ModelTracker", "is_public": true }, { - "name": "unit_horizontal_0", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2328, - "signature": "(self) -> sympy.Matrix", + "name": "live_count", + "kind": "function", + "file": "src/underworld3/ckdtree.pyx", + "line": 28, + "signature": "def live_count():", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "First horizontal direction (alias for unit_horizontal)", + "returns": null, + "existing_docstring": "Number of KDTree instances currently alive on this rank.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_horizontal_1", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2333, - "signature": "(self) -> sympy.Matrix", + "name": "total_constructed", + "kind": "function", + "file": "src/underworld3/ckdtree.pyx", + "line": 32, + "signature": "def total_constructed():", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Second horizontal direction (for 3D systems)", - "harvested_comments": [ - "y-direction in 3D Cartesian", - "tangential direction", - "azimuthal direction" - ], + "returns": null, + "existing_docstring": "Total KDTree instances ever constructed on this rank.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_radial", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2350, - "signature": "(self) -> sympy.Matrix", + "name": "n", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 118, + "signature": "def n(self):", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Radial direction (for cylindrical/spherical coordinate systems)", + "returns": null, + "existing_docstring": "Number of points in the KD-tree.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_tangential", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2365, - "signature": "(self) -> sympy.Matrix", + "name": "ndim", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 123, + "signature": "def ndim(self):", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Tangential direction (for cylindrical coordinate systems)", + "returns": null, + "existing_docstring": "Spatial dimensionality of the KD-tree (2 or 3).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_meridional", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2378, - "signature": "(self) -> sympy.Matrix", + "name": "build_index", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 189, + "signature": "def build_index(self):", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Meridional direction (for spherical coordinate systems)", + "returns": null, + "existing_docstring": "\n Build the kd-tree index.\n", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "unit_azimuthal", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2388, - "signature": "(self) -> sympy.Matrix", + "name": "kdtree_points", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 195, + "signature": "def kdtree_points(self):", "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Azimuthal direction (for spherical coordinate systems)", + "returns": null, + "existing_docstring": "\n Returns a view of the points used to define the kd-tree\n", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "geometric_dimension_names", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2398, - "signature": "(self) -> list", + "name": "find_closest_point", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 204, + "signature": "def find_closest_point(self,\n const double[:,::1] coords not None: numpy.ndarray):", "parameters": [], - "returns": "list", - "existing_docstring": "Names of geometric dimensions for this coordinate system", + "returns": null, + "existing_docstring": "\n Find the points closest to the provided set of coordinates.\n\n Parameters\n ----------\n coords:\n An array of coordinates for which the kd-tree index will be searched for nearest\n neighbours. This should be a 2-dimensional array of size (n_coords,dim).\n\n Returns\n -------\n indices:\n An integer array of indices into the `points` array (passed into the constructor) corresponding to\n the nearest neighbour for the search coordinates. It will be of size (n_coords).\n dist_sqr:\n A float array of squared distances between the provided coords and the nearest neighbouring\n points. It will be of size (n_coords).\n found:\n A bool array of flags which signals whether a nearest neighbour has been found for a given\n coordinate. It will be of size (n_coords).\n\n\n\n", "harvested_comments": [], - "status": "minimal", + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "find_closest_n_points", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 249, + "signature": "def find_closest_n_points(self,\n const int nCount : numpy.int64,\n const double[: ,::1] coords not None: numpy.ndarray):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Find the n points closest to the provided coordinates.\n\n Parameters\n ----------\n nCount:\n The number of nearest neighbour points to find for each `coords`.\n\n coords:\n Coordinates of the points for which the kd-tree index will be searched for nearest\n neighbours. This should be a 2-dimensional array of size (n_coords,dim).\n\n Returns\n -------\n indices:\n An integer array of indices into the `points` array (passed into the constructor) corresponding to\n the nearest neighbour for the search coordinates. It will be of size (n_coords).\n dist_sqr:\n A float array of squared distances between the provided coords and the nearest neighbouring\n points. It will be of size (n_coords).\n\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "query", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 307, + "signature": "def query(self,\n coords,\n k=1,\n sqr_dists=True,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Find the n points closest to the provided coordinates.\n\n This method is unit-aware: if the KD-tree was built with unit-aware coordinates,\n it will automatically convert query coordinates to match and return distances\n with appropriate units.\n\n Parameters\n ----------\n coords : array-like\n An array of coordinates for which the kd-tree index will be searched for nearest\n neighbours. This should be a 2-dimensional array of size (n_coords, dim).\n Can be unit-aware (UnitAwareArray) or plain numpy array.\n If KD-tree has coordinate units, coords must have compatible units.\n k : int, optional\n The number of nearest neighbour points to find for each `coords` (default 1).\n sqr_dists : bool, optional\n Set to True to return the squared distances, set to False to return the actual\n distances (default True).\n\n Returns\n -------\n d : array\n A float array of the squared (sqr_dists = True) or actual distances (sqr_dists = False)\n between the provided coords and the nearest neighbouring points.\n If KD-tree has coordinate units and sqr_dists=False, distances will be unit-aware.\n Shape is (n_coords,) for k=1, or (n_coords, k) for k>1.\n i : array\n An integer array of indices into the `points` array (passed into the constructor)\n corresponding to the nearest neighbour for the search coordinates.\n Shape is (n_coords,) for k=1, or (n_coords, k) for k>1.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "rbf_interpolator_local", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 372, + "signature": "def rbf_interpolator_local(self,\n coords,\n data,\n nnn = 4,\n p=2,\n verbose = False,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Interpolate data to target coordinates using inverse distance weighting.\n\n This is a convenience wrapper around :meth:`rbf_interpolator_local_from_kdtree`.\n It performs radial basis function (RBF) interpolation using inverse distance\n weighting to map known data values from the KD-tree points to arbitrary\n target coordinates.\n\n Parameters\n ----------\n coords : array-like\n Target coordinates where data will be interpolated.\n Shape should be ``(n_coords, dim)``.\n data : ndarray\n Known data values at KD-tree points.\n Shape should be ``(n_points,)`` or ``(n_points, n_components)``.\n nnn : int, optional\n Number of nearest neighbours to use for interpolation (default 4).\n If 1, returns raw nearest-neighbour values without distance weighting.\n p : int, optional\n Power index for distance weighting: ``weight = 1/distance^p`` (default 2).\n verbose : bool, optional\n Print progress messages (default False).\n\n Returns\n -------\n ndarray\n Interpolated data values at target coordinates.\n\n See Also\n --------\n rbf_interpolator_local_from_kdtree : The underlying implementation.\n query : Find nearest neighbours without interpolation.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "old_rbf_interpolator_local_from_kdtree", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 417, + "signature": "def old_rbf_interpolator_local_from_kdtree(self,\n coords,\n data,\n nnn = 4,\n verbose = False,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": "\n An inverse (squared) distance weighted mapping of a numpy array from the\n set of coordinates defined by the kd-tree to the set of input points specified.\n This assumes all points are local to the same processor.\n If that is not the case, it is best to use a particle swarm\n to manage the distributed data.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "primary_directions", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 2415, - "signature": "(self) -> dict", + "name": "old_rbf_interpolator_local_to_kdtree", + "kind": "method", + "file": "src/underworld3/ckdtree.pyx", + "line": 478, + "signature": "def old_rbf_interpolator_local_to_kdtree(self,\n coords,\n data,\n nnn = 4,\n verbose = False,\n weights = None\n ):", "parameters": [], - "returns": "dict", - "existing_docstring": "Dictionary of all available geometric directions for this mesh type", - "harvested_comments": [ - "Add coordinate-system-specific directions" - ], - "status": "minimal", + "returns": null, + "existing_docstring": "\n An inverse (squared) distance weighted mapping of a numpy array to the\n set of coordinates defined by the kd-tree from the set of input points specified.\n This assumes all points are local to the same processor.\n If that is not the case, it is sensible to use a particle swarm\n to manage the distributed data.\n", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "create_line_sample", + "name": "rbf_interpolator_local_from_kdtree", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2463, - "signature": "(self, start_point, direction_vector, length, num_points = 50)", + "file": "src/underworld3/ckdtree.pyx", + "line": 546, + "signature": "def rbf_interpolator_local_from_kdtree(self, coords, data, nnn, p, verbose):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Performs an inverse distance (squared) mapping of data to the target `coords`.\n\n This method is unit-aware: if the KD-tree was built with unit-aware coordinates,\n it will automatically convert query coordinates to match before interpolation.\n\n Parameters\n ----------\n coords : array-like\n The target spatial coordinates to evaluate the data from.\n Can be unit-aware (UnitAwareArray) or plain numpy array.\n If KD-tree has coordinate units, coords must have compatible units.\n coords.shape[1] == self.ndim\n data : ndarray\n The known data to map from. Must be fully described over kd-tree.\n i.e., data.shape[0] == self.n\n nnn : int\n The number of neighbour points to sample from. If `1`, no distance averaging is done.\n p : int\n The power index to calculate weights, i.e., pow(distance, -p)\n verbose : bool\n Print when mapping occurs\n\n Returns\n -------\n ndarray\n Interpolated data values at target coordinates\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "validate_parameters", + "kind": "function", + "file": "src/underworld3/constitutive_models.py", + "line": 167, + "signature": "(symbol, input, default = None, allow_number = True, allow_expression = True)", "parameters": [ { - "name": "start_point", + "name": "symbol", "type_hint": null, "default": null, "description": "" }, { - "name": "direction_vector", + "name": "input", "type_hint": null, "default": null, "description": "" }, { - "name": "length", + "name": "default", "type_hint": null, - "default": null, + "default": "None", "description": "" }, { - "name": "num_points", + "name": "allow_number", "type_hint": null, - "default": "50", + "default": "True", + "description": "" + }, + { + "name": "allow_expression", + "type_hint": null, + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": "Create sample points along a line defined by sympy expressions.\n\nParameters\n----------\nstart_point : list or numpy.ndarray\n Starting point coordinates in Cartesian space\ndirection_vector : sympy.Matrix\n Direction vector (should be unit vector for accurate length)\nlength : float\n Length of the line to sample\nnum_points : int, optional\n Number of sample points to generate\n\nReturns\n-------\ndict\n Dictionary containing:\n - 'cartesian_coords': numpy array of Cartesian coordinates for global_evaluate()\n - 'natural_coords': numpy array of natural coordinates for plotting\n - 'parameters': numpy array of parameter values along the line (0 to length)", + "existing_docstring": "Convert input to a UWexpression for use in constitutive models.\n\nParameters\n----------\nsymbol : str\n LaTeX symbol for display (e.g., r\"\\eta\" for viscosity).\ninput : various\n Value to convert (UWexpression, UWQuantity, float, int, sympy expr).\ndefault : optional\n Default value if input is None.\nallow_number : bool\n If True, accept plain numbers (int/float).\nallow_expression : bool\n If True, accept raw sympy expressions.\n\nReturns\n-------\nUWexpression or None\n Wrapped expression, or None if conversion failed.", "harvested_comments": [ - "Create parameter values along the line", - "Convert start point to numpy array", - "Generate Cartesian coordinates by evaluating the direction vector", - "Get coordinate symbols", - "Current point = start + t * direction" + "CRITICAL: Check for UWexpression FIRST, before checking sympy.Basic", + "UWexpression inherits from sympy.Symbol, so it would match the Basic check", + "and cause double-wrapping, losing unit information", + "Already a UWexpression - return as-is, no wrapping needed", + "Convert UWQuantity to UWexpression - this is the beautiful symmetry!" ], "status": "complete", "needs": [], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": true }, { - "name": "create_profile_sample", + "name": "create_unique_symbol", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2588, - "signature": "(self, profile_type, **params)", + "file": "src/underworld3/constitutive_models.py", + "line": 344, + "signature": "(self, base_symbol, value, description)", "parameters": [ { - "name": "profile_type", + "name": "base_symbol", "type_hint": null, "default": null, "description": "" }, { - "name": "**params", + "name": "value", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Create sample points for common profile types in this coordinate system.\n\nParameters\n----------\nprofile_type : str\n Type of profile to create. Options depend on coordinate system:\n - Cartesian: 'horizontal', 'vertical', 'diagonal'\n - Cylindrical: 'radial', 'tangential', 'vertical'\n - Spherical: 'radial', 'meridional', 'azimuthal'\n**params\n Profile-specific parameters (see individual profile documentation)\n\nReturns\n-------\ndict\n Dictionary containing:\n - 'cartesian_coords': numpy array of Cartesian coordinates for global_evaluate()\n - 'natural_coords': numpy array of natural coordinates for plotting\n - 'parameters': numpy array of parameter values along the profile", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "CoordinateSystem", - "is_public": true - }, - { - "name": "zero_matrix", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2847, - "signature": "(self, shape)", - "parameters": [ + }, { - "name": "shape", + "name": "description", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Matrix of spatial coordinates equivalent to zeros (but still dependent on X) -\nAdd this when you have a matrix with a mix of constants and functions - sympy / numpy\ncan become upset if the constants are not specific functions too.", + "existing_docstring": "Create a unique symbol name for constitutive model parameters.\n\nSymbol naming priority:\n1. If material_name is set: \u03b7 \u2192 \u03b7_{material_name}\n2. Else if multiple instances of same class: \u03b7 \u2192 \u03b7^{(n)}\n3. Else: use base symbol as-is\n\nParameters\n----------\nbase_symbol : str\n The base LaTeX symbol name (e.g., r\"\\eta\", r\"\\kappa\")\nvalue : float or expression\n The initial value for the symbol\ndescription : str\n Description of the parameter\n\nReturns\n-------\nUWexpression\n Expression with unique symbol name", "harvested_comments": [ - "Direct construction to avoid SymPy Matrix scalar multiplication issues" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "Priority 1: User-specified material name (subscript notation)", + "Priority 2: Multiple instances of same class (superscript notation)", + "Priority 3: First/only instance - clean symbol" ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "evaluate", - "kind": "method", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 55, - "signature": "def evaluate(self, verbose=False):", + "name": "Unknowns", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 390, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "\n Evaluate the integral and return the result with units (if applicable).\n\n Returns\n -------\n float or UWQuantity\n The integral value. If the integrand has units AND the mesh coordinates\n have units, returns a UWQuantity with proper dimensional analysis\n (integrand_units * volume_units). Otherwise returns a plain float.\n", + "existing_docstring": "Reference to the solver's unknown fields.\n\nReturns\n-------\nUnknowns\n Container holding the primary unknown field(s) (e.g., velocity,\n pressure, temperature) that this constitutive model operates on.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "dim", - "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 735, - "signature": "(self) -> int", - "parameters": [], - "returns": "int", - "existing_docstring": "Topological dimension of the mesh.\n\nReturns\n-------\nint\n The mesh dimension (2 for 2D, 3 for 3D).", + "name": "Unknowns", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 403, + "signature": "(self, unknowns)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the solver unknowns (invalidates setup).", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "cdim", + "name": "K", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 746, - "signature": "(self) -> int", + "file": "src/underworld3/constitutive_models.py", + "line": 410, + "signature": "(self)", "parameters": [], - "returns": "int", - "existing_docstring": "Coordinate dimension (embedding space dimension).\n\nFor most meshes, ``cdim == dim``. For surface meshes embedded in 3D\n(e.g., a 2D spherical shell), ``dim=2`` but ``cdim=3``.\n\nReturns\n-------\nint\n The coordinate dimension.", + "returns": null, + "existing_docstring": "Primary constitutive property (viscosity, diffusivity, etc.).\n\nReturns\n-------\nUWexpression\n The material property defining the flux-gradient relationship.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "element", + "name": "u", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 760, - "signature": "(self) -> dict", + "file": "src/underworld3/constitutive_models.py", + "line": 421, + "signature": "(self)", "parameters": [], - "returns": "dict", - "existing_docstring": "Element type information for the mesh.\n\nContains details about the finite element discretization including\ncell type, polynomial degree, and quadrature order.\n\nReturns\n-------\ndict\n Element information dictionary.\n\nNotes\n-----\nUW3 does not support mixed-element meshes; this applies uniformly\nto all cells.", + "returns": null, + "existing_docstring": "The primary unknown field from the solver.\n\nReturns\n-------\nMeshVariable\n The unknown field (velocity, temperature, etc.).", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "length_scale", + "name": "grad_u", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 780, - "signature": "(self) -> float", + "file": "src/underworld3/constitutive_models.py", + "line": 432, + "signature": "(self)", "parameters": [], - "returns": "float", - "existing_docstring": "Length scale for non-dimensionalization.\n\nThis property is IMMUTABLE after mesh creation to ensure synchronization\nwith all spatial operators (gradient, divergence, curl, etc.).\n\nThe length scale is derived from model reference quantities at mesh creation:\n- Priority 1: `domain_depth` from `model.set_reference_quantities()`\n- Priority 2: `length` from `model.set_reference_quantities()`\n- Default: 1.0 (no scaling)\n\nReturns\n-------\nfloat\n Length scale value for non-dimensionalization\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_depth=uw.quantity(100, \"km\"))\n>>> mesh = uw.meshing.UnstructuredSimplexBox(...)\n>>> mesh.length_scale\n100000.0 # meters\n\nSee Also\n--------\nlength_units : Units string for length scale", - "harvested_comments": [], + "returns": null, + "existing_docstring": "Gradient of the unknown field.\n\nFor scalar fields, this is a vector. For vector fields (velocity),\nthis is the velocity gradient tensor :math:`\\nabla \\mathbf{u}`.\n\nReturns\n-------\nsympy.Matrix\n Gradient/Jacobian of the unknown field.", + "harvested_comments": [ + "return mesh.vector.gradient(self.Unknowns.u.sym)" + ], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "length_units", + "name": "DuDt", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 811, - "signature": "(self) -> str", + "file": "src/underworld3/constitutive_models.py", + "line": 448, + "signature": "(self)", "parameters": [], - "returns": "str", - "existing_docstring": "Unit string for the length scale.\n\nReturns\n-------\nstr\n Units for the length scale (e.g., \"meter\", \"kilometer\")\n\nExamples\n--------\n>>> mesh.length_units\n'kilometer'", + "returns": null, + "existing_docstring": "Material derivative operator for the unknown field.\n\nUsed in time-dependent problems to track Lagrangian or\nsemi-Lagrangian derivatives.\n\nReturns\n-------\nSemiLagrangian_DDt or Lagrangian_DDt or None\n The material derivative operator, or None if not set.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "view", + "name": "DuDt", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 827, - "signature": "(self, level = 0)", + "file": "src/underworld3/constitutive_models.py", + "line": 462, + "signature": "(self, DuDt_value: Union[SemiLagrangian_DDt, Lagrangian_DDt])", "parameters": [ { - "name": "level", - "type_hint": null, - "default": "0", + "name": "DuDt_value", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Displays mesh information at different levels.\n\nParameters\n----------\nlevel : int (0 default)\n The display level.\n 0, for basic mesh information (variables and boundaries), while level=1 displays detailed mesh information (including PETSc information)", - "harvested_comments": [ - "{self.instance}: {self.name}\\n\")", - "Display coordinate units if set", - "Display length scale for non-dimensionalization", - "Display coordinate system information", - "Show available coordinate accessors" - ], - "status": "partial", + "existing_docstring": "Set the material derivative operator for the unknown.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "view_parallel", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1020, + "name": "DFDt", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 472, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "returns the break down of boundary labels from each processor", - "harvested_comments": [ - "{self.instance}: {self.name}\\n\")", - "# Boundary information on each proc", - "## goes through each processor and gets the label size" - ], + "existing_docstring": "Material derivative operator for the flux history.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "clone_dm_hierarchy", + "name": "DFDt", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1071, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/constitutive_models.py", + "line": 478, + "signature": "(self, DFDt_value: Union[SemiLagrangian_DDt, Lagrangian_DDt])", + "parameters": [ + { + "name": "DFDt_value", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Clone the dm hierarchy on the mesh", + "existing_docstring": "Set the material derivative operator for flux history.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "update_lvec", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1189, + "name": "C", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 490, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "This method creates and/or updates the mesh variable local vector.\nIf the local vector is already up to date, this method will do nothing.", - "harvested_comments": [ - "create the local vector (memory chunk) and attach to original dm", - "push avar arrays into the parent dm array", - "The field decomposition seems to fail if coarse DMs are present", - "traverse subdms, taking user generated data in the subdm", - "local vec, pushing it into a global sub vec" - ], - "status": "minimal", + "existing_docstring": "The matrix form of the constitutive model (the `c` property)\nthat relates fluxes to gradients.\nFor scalar problem, this is the matrix representation of the rank 2 tensor.\nFor vector problems, the Mandel form of the rank 4 tensor is returned.\nNOTE: this is an immutable object that is _a view_ of the underlying tensor", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "lvec", + "name": "c", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1228, - "signature": "(self) -> PETSc.Vec", + "file": "src/underworld3/constitutive_models.py", + "line": 509, + "signature": "(self)", "parameters": [], - "returns": "PETSc.Vec", - "existing_docstring": "Returns a local Petsc vector containing the flattened array\nof all the mesh variables.", + "returns": null, + "existing_docstring": "The tensor form of the constitutive model that relates fluxes to gradients. In scalar\nproblems, `c` and `C` are equivalent (matrices), but in vector problems, `c` is a\nrank 4 tensor. NOTE: `c` is the canonical form of the constitutive relationship.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "access", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1371, - "signature": "(self, *writeable_vars)", - "parameters": [ - { - "name": "*writeable_vars", - "type_hint": "'MeshVariable'", - "default": null, - "description": "" - } - ], + "name": "flux", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 523, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Dummy access manager that provides deferred sync for backward compatibility.\nUses NDArray_With_Callback.delay_callbacks_global() internally.\n\nThis is a compatibility wrapper that allows existing code using the access()\ncontext manager to work with the new direct-access variable interfaces.\nAll variable modifications are deferred and synchronized at context exit.\n\nParameters\n----------\nwriteable_vars\n Variables that will be modified (ignored - all variables are writable\n with the new interface, this parameter is kept for API compatibility)\n\nReturns\n-------\nContext manager that defers variable synchronization until exit\n\nNotes\n-----\nThis method is deprecated. New code should access variable.data or\nvariable.array directly without requiring an access context.", - "harvested_comments": [ - "Use NDArray_With_Callback global delay context for deferred sync", - "This triggers all accumulated callbacks from all variables" + "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "N", + "name": "flux_1d", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1421, - "signature": "(self) -> sympy.vector.CoordSys3D", + "file": "src/underworld3/constitutive_models.py", + "line": 554, + "signature": "(self)", "parameters": [], - "returns": "sympy.vector.CoordSys3D", - "existing_docstring": "SymPy coordinate system for symbolic calculus.\n\nThe base coordinate system used for gradient, divergence, and\ncurl operations. Access base scalars via ``mesh.N.x``, ``mesh.N.y``,\n``mesh.N.z`` and base vectors via ``mesh.N.i``, ``mesh.N.j``, ``mesh.N.k``.\n\nReturns\n-------\nsympy.vector.CoordSys3D\n The SymPy coordinate system object.\n\nSee Also\n--------\nX : Coordinate system with data access.\nr : Tuple of coordinate scalars.", + "returns": null, + "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux. Returns the Voigt form that is flattened so as to\nmatch the PETSc field storage pattern for symmetric tensors.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "Gamma_N", + "name": "flux_jacobian", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1441, - "signature": "(self) -> sympy.vector.CoordSys3D", + "file": "src/underworld3/constitutive_models.py", + "line": 577, + "signature": "(self)", "parameters": [], - "returns": "sympy.vector.CoordSys3D", - "existing_docstring": "SymPy coordinate system for boundary/surface coordinates.\n\nReturns\n-------\nsympy.vector.CoordSys3D\n The boundary coordinate system object.", + "returns": null, + "existing_docstring": "Optional smooth surrogate flux for Jacobian assembly.\n\nReturns ``None`` by default, meaning the solver differentiates the\nexact :attr:`flux` (the Newton fix unwraps it first; a generic Min/Max\nkink-smoothing fallback then rounds any remaining yield kink).\n\nA model whose flux has a non-smooth yield kink (e.g. hard-``Min``\nviscoplasticity) may override this to supply a physically-motivated\n*smooth constitutive law for the tangent only*. The residual still uses\nthe exact :attr:`flux`, so the converged solution satisfies the true\nconstitutive law \u2014 only the Newton search direction is smoothed, giving\na robust, line-search-friendly tangent without changing the answer.\n\nShape must match :attr:`flux` (the solver substitutes it for ``F1`` when\nforming the velocity-gradient Jacobian blocks).", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "Gamma", + "name": "requires_stress_history", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1452, - "signature": "(self) -> sympy.vector.CoordSys3D", + "file": "src/underworld3/constitutive_models.py", + "line": 614, + "signature": "(self)", "parameters": [], - "returns": "sympy.vector.CoordSys3D", - "existing_docstring": "Boundary coordinate scalars as a row matrix.\n\nReturns\n-------\nsympy.Matrix\n Row matrix of boundary coordinate scalars.", + "returns": null, + "existing_docstring": "Whether this model needs DFDt stress history tracking.\n\nModels that return True require a solver with stress history\nmanagement (e.g. VE_Stokes). Assigning such a model to a plain\nStokes solver will raise an error.", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "X", + "name": "stress_history_ddt_kwargs", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1463, + "file": "src/underworld3/constitutive_models.py", + "line": 624, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Coordinate system with symbolic coordinates and data access.\n\nThe primary interface for mesh coordinates, providing both symbolic\nexpressions for equations and numerical data for evaluation.\n\nReturns\n-------\nCoordinateSystem\n Coordinate system object with:\n\n - ``mesh.X[0]``, ``mesh.X[1]``: Symbolic coordinate functions\n - ``mesh.X.coords``: Coordinate data array (vertex positions)\n - ``mesh.X.units``: Coordinate units\n - ``x, y = mesh.X``: Unpack symbolic coordinates\n\nExamples\n--------\n>>> x, y = mesh.X # Symbolic coordinates for equations\n>>> coords = mesh.X.coords # Numerical vertex positions\n\nSee Also\n--------\nN : SymPy coordinate system for vector calculus.", - "harvested_comments": [ - "Symbolic coordinates for equations", - "Numerical vertex positions" + "existing_docstring": "Extra kwargs passed to the auto-DDt creation when this model\ntriggers it via ``requires_stress_history = True``.\n\nDefault: empty dict (BDF-only models). ETD-2 / exponential models\noverride to inject ``with_forcing_history=True`` so the DDt\nallocates a forcing-history slot.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "Constitutive_Model", + "is_public": true + }, + { + "name": "plastic_fraction", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 655, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Fraction of strain rate that is plastic (0 for non-plastic models).\n\nReturns a sympy expression that can be evaluated post-solve via\n``uw.function.evaluate(cm.plastic_fraction, coords)``.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "Constitutive_Model", "is_public": true }, { - "name": "CoordinateSystem", + "name": "viscosity", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1491, - "signature": "(self) -> CoordinateSystem", + "file": "src/underworld3/constitutive_models.py", + "line": 781, + "signature": "(self)", "parameters": [], - "returns": "CoordinateSystem", - "existing_docstring": "Alias for :attr:`X` (the coordinate system object).", + "returns": null, + "existing_docstring": "Whatever the consistutive model defines as the effective value of viscosity\nin the form of an uw.expression", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscousFlowModel", "is_public": true }, { - "name": "r", + "name": "K", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1496, - "signature": "(self) -> Tuple[sympy.vector.BaseScalar]", + "file": "src/underworld3/constitutive_models.py", + "line": 788, + "signature": "(self)", "parameters": [], - "returns": "Tuple[sympy.vector.BaseScalar]", - "existing_docstring": "Tuple of coordinate scalars :math:`(x, y)` or :math:`(x, y, z)`.\n\nReturns\n-------\ntuple\n Tuple of SymPy base scalars ``(N.x, N.y[, N.z])``.\n\nSee Also\n--------\nrvec : Position vector form.", + "returns": null, + "existing_docstring": "Effective stiffness parameter (viscosity for viscous flow)", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscousFlowModel", "is_public": true }, { - "name": "rvec", + "name": "flux", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1511, - "signature": "(self) -> sympy.vector.Vector", + "file": "src/underworld3/constitutive_models.py", + "line": 793, + "signature": "(self)", "parameters": [], - "returns": "sympy.vector.Vector", - "existing_docstring": "Position vector :math:`\\mathbf{r} = x\\hat{i} + y\\hat{j} [+ z\\hat{k}]`.\n\nReturns\n-------\nsympy.vector.Vector\n The position vector in the mesh coordinate system.", + "returns": null, + "existing_docstring": "Viscous stress tensor: :math:`\\boldsymbol{\\tau} = 2\\eta\\dot{\\varepsilon}`.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscousFlowModel", "is_public": true }, { - "name": "data", + "name": "grad_u", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1531, - "signature": "(self) -> numpy.ndarray", + "file": "src/underworld3/constitutive_models.py", + "line": 822, + "signature": "(self)", "parameters": [], - "returns": "numpy.ndarray", - "existing_docstring": "The array of mesh element vertex coordinates.\n\n.. deprecated:: 0.99.0\n Use :attr:`X.coords` instead.\n ``mesh.data`` is deprecated in favor of ``mesh.X.coords``\n (coordinate-system-aware interface).\n\nThis is an alias for mesh.points (which is also deprecated).", + "returns": null, + "existing_docstring": "Symmetric strain rate tensor (with 1/2 factor).\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "ViscousFlowModel", "is_public": true }, { - "name": "points", + "name": "plastic_fraction", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1550, + "file": "src/underworld3/constitutive_models.py", + "line": 838, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Mesh node coordinates in physical units.\n\n.. deprecated:: 0.99.0\n Use :attr:`X.coords` instead.\n ``mesh.points`` is deprecated in favor of ``mesh.X.coords``\n (coordinate-system-aware interface).\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from internal model coordinates\nto physical coordinates for user access.\n\nWhen the mesh has coordinate units specified, returns a unit-aware array.\n\nReturns:\n numpy.ndarray or UnitAwareArray: Node coordinates (with units if specified)", - "harvested_comments": [ - "Apply scaling to convert model coordinates to physical coordinates", - "Wrap with unit-aware array if units are specified" - ], - "status": "partial", + "existing_docstring": "Fraction of strain rate that is plastic: 1 - \u03b7_vp / \u03b7_viscous.", + "harvested_comments": [], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscousFlowModel", "is_public": true }, { - "name": "points", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1592, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1021, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Set mesh node coordinates from physical units.\n\n.. deprecated:: 0.99.0\n Use :attr:`X.coords` instead.\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from physical coordinates\nto internal model coordinates for PETSc storage.\n\nArgs:\n value (numpy.ndarray or UnitAwareArray): Node coordinates in physical units", + "existing_docstring": "Effective viscosity with plastic yielding.\n\n.. math::\n \\eta_{\\mathrm{eff}} = \\min\\left(\\eta_0, \\frac{\\tau_y}{2\\dot{\\varepsilon}_{II}}\\right)\n\nwhere :math:`\\dot{\\varepsilon}_{II}` is the second invariant of strain rate.", "harvested_comments": [ - "PRINCIPLE (2025-11-27): When units are active, require unit-aware input", - "to avoid ambiguity about whether values are dimensional or non-dimensional.", - "Plain array assigned when units are active - ambiguous", - "Handle unit-aware input", - "Extract numerical value from unit-aware object" + "detect if values we need are defined or are placeholder symbols", + "Don't put conditional behaviour in the constitutive law", + "when it is not needed", + "# Question is, will sympy reliably differentiate something", + "# with so many Max / Min statements. The smooth version would" ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "ViscoPlasticFlowModel", "is_public": true }, { - "name": "physical_coordinates", - "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1661, - "signature": "(self)", + "name": "plastic_correction", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1071, + "signature": "(self) -> float", "parameters": [], - "returns": null, - "existing_docstring": "Mesh coordinates in physical units.\n\nReturns the mesh coordinate array scaled to physical units using\nthe model's length scale. Requires the mesh to be associated with\na model that has reference quantities set.\n\nReturns\n-------\nUWQuantity or None\n Coordinates in physical units, or None if no model scaling available\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_length=1000*uw.units.km, ...)\n>>> mesh = uw.meshing.StructuredQuadBox(...)\n>>> physical_coords = mesh.physical_coordinates # In kilometers", + "returns": "float", + "existing_docstring": "Scaling factor to reduce stress to yield surface.\n\n.. math::\n f = \\frac{\\tau_y}{\\tau_{II}}\n\nwhere :math:`\\tau_{II}` is the second invariant of deviatoric stress.\nReturns 1 if no yield stress is set.", "harvested_comments": [ - "In kilometers" + "The yield criterion in this case is assumed to be a bound on the second invariant of the stress" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoPlasticFlowModel", "is_public": true }, { - "name": "physical_bounds", + "name": "dt_elastic", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1686, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Mesh bounds in physical units.\n\nReturns the mesh bounding box scaled to physical units using\nthe model's length scale.\n\nReturns\n-------\ntuple of UWQuantity or None\n (min_coords, max_coords) in physical units, or None if no model scaling\n\nExamples\n--------\n>>> physical_min, physical_max = mesh.physical_bounds\n>>> print(f\"Domain: {physical_min} to {physical_max}\")", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/constitutive_models.py", + "line": 1268, + "signature": "(inner_self)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "Mesh", - "is_public": true - }, - { - "name": "physical_extent", - "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1717, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Mesh spatial extent in physical units.\n\nReturns the mesh size (max - min) in each dimension scaled to physical units.\n\nReturns\n-------\nUWQuantity or None\n Extent in physical units, or None if no model scaling\n\nExamples\n--------\n>>> extent = mesh.physical_extent\n>>> print(f\"Domain size: {extent}\")", + "existing_docstring": "Timestep for VE formulas. Set by the solver, not a user parameter.\n\nReturns the UWexpression that the solver updates before each solve.\nThis flows through PetscDS constants[] so the JIT-compiled pointwise\nfunctions always see the current timestep.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "write_timestep", + "name": "dt_elastic", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1745, - "signature": "(self, filename: str, index: int, outputPath: Optional[str] = '', meshVars: Optional[list] = [], swarmVars: Optional[list] = [], meshUpdates: bool = False)", + "file": "src/underworld3/constitutive_models.py", + "line": 1278, + "signature": "(inner_self, value)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "inner_self", + "type_hint": null, "default": null, "description": "" }, { - "name": "index", - "type_hint": "int", + "name": "value", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" - }, - { - "name": "meshVars", - "type_hint": "Optional[list]", - "default": "[]", - "description": "" - }, - { - "name": "swarmVars", - "type_hint": "Optional[list]", - "default": "[]", - "description": "" - }, - { - "name": "meshUpdates", - "type_hint": "bool", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Write the selected mesh, variables and swarm variables (as proxies) for later visualisation.\nAn xdmf file is generated and the overall package can then be read by paraview or pyvista.\nVertex values (on the mesh points) are stored for all variables regardless of their interpolation order", - "harvested_comments": [ - "check the directory where we will write checkpoint", - "get directory", - "check if path exists", - "easier to debug abs", - "check if we have write access" - ], + "existing_docstring": "Allow the solver to set dt via Parameters.dt_elastic = timestep.", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "petsc_save_checkpoint", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1816, - "signature": "(self, index: int, meshVars: Optional[list] = [], outputPath: Optional[str] = '')", + "name": "ve_effective_viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1349, + "signature": "(inner_self)", "parameters": [ { - "name": "index", - "type_hint": "int", + "name": "inner_self", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "meshVars", - "type_hint": "Optional[list]", - "default": "[]", - "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" } ], "returns": null, - "existing_docstring": "Use PETSc to save the mesh and mesh vars in a h5 and xdmf file.\n\nParameters\n----------\nmeshVars:\n List of UW mesh variables to save. If left empty then just the mesh is saved.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.", + "existing_docstring": "Visco-elastic effective viscosity: :math:`\\eta_{\\mathrm{eff}} = \\frac{\\eta G \\Delta t}{\\eta + G \\Delta t}`.", "harvested_comments": [ - "## save mesh vars", - "### create petsc viewer", - "## Empty meshVars will save just the mesh" + "the dt_elastic defaults to infinity, t_relax to zero,", + "so this should be well behaved in the viscous limit", + "BDF-k effective viscosity: eta_eff = eta*mu*dt / (c0*eta + mu*dt)", + "c0 is a UWexpression routed through PetscDS constants[],", + "updated each step by _update_bdf_coefficients()." ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "write_checkpoint", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1862, - "signature": "(self, filename: str, meshUpdates: bool = True, meshVars: Optional[list] = [], swarmVars: Optional[list] = [], index: Optional[int] = 0, unique_id: Optional[bool] = False)", + "name": "t_relax", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1372, + "signature": "(inner_self)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "inner_self", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "meshUpdates", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "meshVars", - "type_hint": "Optional[list]", - "default": "[]", - "description": "" - }, - { - "name": "swarmVars", - "type_hint": "Optional[list]", - "default": "[]", - "description": "" - }, - { - "name": "index", - "type_hint": "Optional[int]", - "default": "0", - "description": "" - }, - { - "name": "unique_id", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Write data in a format that can be restored for restarting the simulation\nThe difference between this and the visualisation is 1) the parallel section needs\nto be stored to reload the data correctly, and 2) the visualisation information (vertex form of fields)\nis not stored. This routines uses dmplex *VectorView and *VectorLoad functionality", + "existing_docstring": "Maxwell relaxation time: :math:`t_{\\mathrm{relax}} = \\eta / G`.", "harvested_comments": [ - "The mesh checkpoint is the same as the one required for visualisation", - "Checkpoint file", - "Store the parallel-mesh section information for restoring the checkpoint.", - "v._gvec.view(viewer) # would add viz information plus a duplicate of the data", - "should not be required" + "shear modulus defaults to infinity so t_relax goes to zero", + "in the viscous limit" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "write", + "name": "order", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1383, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Time integration order (1 or 2).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": true + }, + { + "name": "order", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1924, - "signature": "(self, filename: str, index: Optional[int] = None)", + "file": "src/underworld3/constitutive_models.py", + "line": 1388, + "signature": "(self, value)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "value", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "index", - "type_hint": "Optional[int]", - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": "Save mesh data to the specified hdf5 file.\n\n\nParameters\n----------\nfilename :\n The filename for the mesh checkpoint file.\nindex :\n Not yet implemented. An optional index which might\n correspond to the timestep (for example).", + "existing_docstring": "Set the time integration order.\n\nIf the model is already attached to a solver with a DFDt, this will\nwarn if the DFDt was created with a lower order (since it can't be\nchanged after creation \u2014 the DFDt allocates history buffers at init).", "harvested_comments": [ - "# JM:To enable timestep recording, the following needs to be called.", - "# I'm unsure if the corresponding xdmf functionality is enabled via", - "# the PETSc xdmf script.", - "viewer.pushTimestepping(viewer)", - "viewer.setTimestep(index)" + "Propagate to connected solver if present" ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "vtk", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1989, - "signature": "(self, filename: str)", - "parameters": [ - { - "name": "filename", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "name": "effective_order", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1419, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Save mesh to the specified file", + "existing_docstring": "Effective order accounting for DDt history startup.\n\nDuring the first few timesteps, the DDt may not have enough history\nto support the requested order. This property returns the lower of\nthe requested order and the DDt's effective order (which ramps from\n1 to self.order as history accumulates).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "generate_xdmf", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1998, - "signature": "(self, filename: str)", - "parameters": [ - { - "name": "filename", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "name": "stress_history_ddt_kwargs", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1532, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "This method generates an xdmf schema for the specified file.\n\nThe filename of the generated file will be the same as the hdf5 file\nbut with the `xmf` extension.\n\nParameters\n----------\nfilename :\n File name of the checkpointed hdf5 file for which the\n xdmf schema will be written.", + "existing_docstring": "SemiLagrangian DDt kwargs based on integrator selection.\n\nETD-2 (order=2) needs the forcing-history slot; BDF and ETD-1\n(order=1) do not.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "vars", + "name": "stress_star", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2020, + "file": "src/underworld3/constitutive_models.py", + "line": 1544, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "A list of variables recorded on the mesh.", + "existing_docstring": "Previous timestep stress :math:`\\boldsymbol{\\sigma}^*` from history.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "block_vars", + "name": "stress_2star", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2029, + "file": "src/underworld3/constitutive_models.py", + "line": 1552, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "A list of variables recorded on the mesh.", - "harvested_comments": [], + "existing_docstring": "Second-order stress history :math:`\\boldsymbol{\\sigma}^{**}` (for 2nd order integration).", + "harvested_comments": [ + "Check if we have enough information in DFDt to update _stress_star,", + "otherwise it will be defined as zero" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "points_in_domain", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2475, - "signature": "(self, points, strict_validation = True)", - "parameters": [ - { - "name": "points", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "strict_validation", - "type_hint": null, - "default": "True", - "description": "" - } - ], + "name": "E_eff", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1566, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Determine if the given points lie in this domain.\nUses a mesh-boundary skeletonization array to determine whether the point is\ninside the boundary or outside. If close to the boundary, it checks if points\nare in a cell.\n\nParameters\n----------\npoints : array-like\n Coordinate array in any physical unit system (will be auto-converted).\n Plain numbers are assumed to be in model coordinates.\nstrict_validation : bool\n Whether to perform strict validation near boundaries", + "existing_docstring": "Effective strain rate including elastic-history coupling.\n\nFor BDF integration:\n\n.. math::\n \\dot{\\varepsilon}_\\mathrm{eff} = \\dot{\\varepsilon}\n - \\sum_i c_i \\frac{\\sigma^{*(i)}}{2 \\mu \\Delta t}\n\nFor ETD-2 (exponential) integration:\n\n.. math::\n \\dot{\\varepsilon}_\\mathrm{eff} = (1-\\varphi)\\,\\dot{\\varepsilon}\n + \\frac{\\alpha}{2\\eta}\\,\\sigma^*\n + (\\varphi-\\alpha)\\,\\dot{\\varepsilon}^*\n\nBoth forms reduce to bare ``\u03b5\u0307`` when no elastic history is\nactive. The yield criterion ``\u03b7_pl = \u03c4_y/(2|E_eff|_II)`` is the\nsame expression structure for both \u2014 it adapts naturally to the\nintegrator's ``E_eff``.", "harvested_comments": [ - "Convert points to model coordinates using the unified conversion function", - "This handles all coordinate formats: plain numbers, unit-aware coordinates, lists, tuples, arrays", - "_convert_coords_to_si now converts to model coordinates (despite the name)", - "and handles all the complexity of extracting values from unit-aware coordinates", - "# This choice of distance needs some more thought" + "ETD-2 effective strain rate carrying \u03b1\u00b7\u03c3*/(2\u03b7) and (\u03c6-\u03b1)\u00b7\u03b5\u0307*.", + "ETD-1 (order=1): \u03c6 = \u03b1 (set in _update_history_coefficients),", + "so the (\u03c6-\u03b1)\u00b7\u03b5\u0307* term zeros out and (1-\u03c6)\u00b7\u03b5\u0307 \u2192 (1-\u03b1)\u00b7\u03b5\u0307 \u2014 same", + "expression tree, no separate code path needed.", + "BDF default" ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "get_closest_cells", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2529, - "signature": "(self, coords: numpy.ndarray) -> numpy.ndarray", - "parameters": [ - { - "name": "coords", - "type_hint": "numpy.ndarray", - "default": null, - "description": "" - } + "name": "E_eff_inv_II", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1624, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Second invariant of effective strain rate: :math:`\\dot{\\varepsilon}_{II} = \\sqrt{\\frac{1}{2}\\dot{\\varepsilon}_{ij}\\dot{\\varepsilon}_{ij}}`.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "returns": "numpy.ndarray", - "existing_docstring": "This method uses a kd-tree algorithm to find the closest\ncells to the provided coords. For a regular mesh, this should\nbe exactly the owning cell, but if the mesh is deformed, this\nis not guaranteed. Note, the nearest point may not be all\nthat close by - use get_closest_local_cells to filter out points\nthat are (probably) not within any local cell.\n\nParameters:\n-----------\ncoords:\n An array of the coordinates for which we wish to determine the\n closest cells. This should be a 2-dimensional array of\n shape (n_coords,dim) in any physical unit system (will be auto-converted).\n Plain numbers are assumed to be in model coordinates.\n\nReturns:\n--------\nclosest_cells:\n An array of indices representing the cells closest to the provided\n coordinates. This will be a 1-dimensional array of\n shape (n_coords).", - "harvested_comments": [ - "Convert coords to model coordinates", - "Simply extract raw values - np.asarray handles unit-aware objects correctly", - "## returns an empty 1D array if no coords are provided", - "CRITICAL: Must return 1D array, not 2D, for Cython buffer compatibility" + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": true + }, + { + "name": "K", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1632, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Effective stiffness parameter (viscosity for visco-elastic-plastic flow).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "test_if_points_in_cells", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2646, - "signature": "(self, points, cells)", - "parameters": [ - { - "name": "points", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "cells", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1651, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Determine if the given points lie in the suggested cells.\nUses a mesh skeletonization array to determine whether the point is\nwith the convex polygon / polyhedron defined by a cell.\n\nExact if applied to a linear mesh, approximate otherwise.\n\nParameters\n----------\npoints : array-like\n Coordinate array in any physical unit system (will be auto-converted)\ncells : array-like\n Cell indices to test\n\nReturns\n-------\nnumpy.ndarray\n Boolean array indicating if points are in cells", + "existing_docstring": "Effective viscosity combining visco-elastic and plastic limits.\n\nThe yield mode controls how \u03b7_ve and \u03b7_pl are combined:\n\n- ``\"smooth\"`` (default): corrected harmonic ``\u03b7_ve\u00b7(1+f)/(1+f+f\u00b2)``\n where ``f = \u03b7_ve/\u03b7_pl``. Converges to \u03b7_pl at deep yielding,\n no Min/Max discontinuities.\n- ``\"harmonic\"``: ``1/(1/\u03b7_ve + 1/\u03b7_pl)``. Smooth but undershoots \u03c4_y\n when \u03b7_ve is small relative to \u03b7_pl.\n- ``\"min\"``: sharp ``Min(\u03b7_ve, \u03b7_pl)``. Exact yield stress but can\n cause SNES divergence with higher-order BDF time integration.\n\nThe unclipped \u03b7_ve depends on ``self._integrator`` \u2014\n:py:attr:`_unclipped_ve_viscosity` returns the correct base for\nBDF or ETD-2 (raw \u03b7 for ETD-2 since the time factor lives in\n``E_eff``; ``ve_effective_viscosity`` for BDF).", "harvested_comments": [ - "Convert points to model units using the elegant protocol", - "Extract numerical values for internal mesh operations", - "Call internal implementation" + "Smooth approximation to Min(\u03b7_ve, \u03b7_pl):", + "\u03b7_eff = \u03b7_ve / g(f)", + "g(f) = 1 + softplus(f-1) - softplus(-1) \u2248 max(1, f)", + "where softplus(x) = (x + \u221a(x\u00b2 + \u03b4\u00b2))/2 and f = \u03b7_ve/\u03b7_pl.", + "Corrected so g(0) = 1 exactly (no spurious yield below onset)." ], - "status": "complete", - "needs": [], - "parent_class": "Mesh", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "get_closest_local_cells", + "name": "plastic_correction", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2681, - "signature": "(self, coords: numpy.ndarray) -> numpy.ndarray", - "parameters": [ - { - "name": "coords", - "type_hint": "numpy.ndarray", - "default": null, - "description": "" - } + "file": "src/underworld3/constitutive_models.py", + "line": 1756, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Scaling factor to reduce stress to yield surface: :math:`f = \\tau_y / \\tau_{II}`.", + "harvested_comments": [ + "The yield criterion in this case is assumed to be a bound on the second invariant of the stress" ], - "returns": "numpy.ndarray", - "existing_docstring": "This method uses a kd-tree algorithm to find the closest\ncells to the provided coords. For a regular mesh, this should\nbe exactly the owning cell, but if the mesh is deformed, this\nis not guaranteed. Also compares the distance from the cell to the\npoint - if this is larger than the \"cell size\" then returns -1\n\nParameters:\n-----------\ncoords:\n An array of the coordinates for which we wish to determine the\n closest cells. This should be a 2-dimensional array of\n shape (n_coords,dim) in any physical unit system (will be auto-converted).\n\nReturns:\n--------\nclosest_cells:\n An array of indices representing the cells closest to the provided\n coordinates. This will be a 1-dimensional array of\n shape (n_coords).", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": true + }, + { + "name": "flux", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1814, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux. For viscoelasticity, the", "harvested_comments": [ - "Convert coords to model units using the elegant protocol", - "Extract numerical values for internal mesh operations", - "Call internal implementation" + "if self.is_viscoplastic:", + "plastic_scale_factor = sympy.Max(1, self.plastic_overshoot())", + "stress /= plastic_scale_factor" ], - "status": "complete", - "needs": [], - "parent_class": "Mesh", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "get_min_radius_old", + "name": "stress_projection", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2779, - "signature": "(self) -> float", + "file": "src/underworld3/constitutive_models.py", + "line": 1828, + "signature": "(self)", "parameters": [], - "returns": "float", - "existing_docstring": "This method returns the global minimum distance from any cell centroid to a face.\nIt wraps to the PETSc `DMPlexGetMinRadius` routine. The petsc4py equivalent always\nreturns zero.", + "returns": null, + "existing_docstring": "viscoelastic stress projection (no plastic response)", "harvested_comments": [ - "# Note: The petsc4py version of DMPlexComputeGeometryFVM does not compute all cells and", - "# does not obtain the minimum radius for the mesh." + "This is a scalar viscosity ..." ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "get_min_radius", + "name": "stress", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2796, - "signature": "(self) -> float", + "file": "src/underworld3/constitutive_models.py", + "line": 1850, + "signature": "(self)", "parameters": [], - "returns": "float", - "existing_docstring": "This method returns the global minimum distance from any cell centroid to a face.\nIt wraps to the PETSc `DMPlexGetMinRadius` routine. The petsc4py equivalent always\nreturns zero.", + "returns": null, + "existing_docstring": "Viscoelastic(-plastic) deviatoric stress for the weak form.\n\nBoth BDF and ETD-2 are written as ``\u03c3 = 2\u00b7viscosity\u00b7E_eff``.\n:py:attr:`E_eff` carries the integrator-specific elastic-history\ncoupling, and :py:attr:`viscosity` returns the appropriate yield-\nwrapped effective viscosity (``ve_effective_viscosity`` for BDF,\nraw ``\u03b7`` for ETD-2 since the time factor is in ``E_eff``).", "harvested_comments": [ - "# Note: The petsc4py version of DMPlexComputeGeometryFVM does not compute all cells and", - "# does not obtain the minimum radius for the mesh." + "ETD-1 (order=1) uses the same E_eff machinery but with \u03c6=\u03b1, so", + "forcing_star is not required (the (\u03c6-\u03b1)\u00b7\u03b5\u0307* term zeros out).", + "Only ETD-2 (order=2) needs forcing_star." ], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "get_max_radius", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2812, - "signature": "(self) -> float", + "name": "yield_mode", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1934, + "signature": "(self)", "parameters": [], - "returns": "float", - "existing_docstring": "This method returns the global maximum distance from any cell centroid to a face.", - "harvested_comments": [ - "# Note: The petsc4py version of DMPlexComputeGeometryFVM does not compute all cells and", - "# does not obtain the minimum radius for the mesh." + "returns": null, + "existing_docstring": "How to combine VE and plastic viscosities.\n\n``\"softmin\"`` (default): smooth approximation to Min \u2014\n ``\u03b7_ve / g(f)`` where ``g(f) \u2248 max(1, f)`` with smoothing\n parameter \u03b4 (``yield_softness``, default 0.1). Approaches\n exact Min as \u03b4 \u2192 0; smooth derivatives at the kink.\n Recommended default: gets within ~2 % of the true yield\n surface while avoiding the SNES kink penalties of ``\"min\"``.\n``\"harmonic\"``: parallel blending \u2014 ``1/(1/\u03b7_ve + 1/\u03b7_pl)``.\n Smooth but undershoots \u03c4_y for soft materials.\n``\"min\"``: sharp cutoff \u2014 ``Min(\u03b7_ve, \u03b7_pl)``.\n Exact yield but can cause SNES divergence with BDF-2 and\n BDF-2 phase-lag at BC discontinuities (see benchmarks).\n\nNote: the previous ``\"smooth\"`` mode (corrected harmonic\n``\u03b7_ve\u00b7(1+f)/(1+f+f\u00b2)``) was retired \u2014 it under-clipped the\nyield surface by ~50 % under realistic forcing, with no\ncompensating benefit over ``softmin``. Recover from git\nhistory if needed (commit message keyword: ``smooth_yield``).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": true + }, + { + "name": "yield_softness", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1973, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Regularisation parameter \u03b4 for ``\"softmin\"`` yield mode.\n\nControls how closely the soft minimum approximates the sharp Min.\nSmaller values \u2192 sharper yield (closer to Min, less robust).\nLarger values \u2192 smoother transition (more robust, lower stress).\n\nDefault 0.1. Only used when ``yield_mode == \"softmin\"``.\nIncrease toward 0.5 if SNES convergence is difficult at yield onset.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": true + }, + { + "name": "requires_stress_history", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1991, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "VEP models always require stress history tracking.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "stats", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2827, - "signature": "(self, uw_function, uw_meshVariable, basis = None)", - "parameters": [ - { - "name": "uw_function", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "uw_meshVariable", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "basis", - "type_hint": null, - "default": "None", - "description": "" - } + "name": "plastic_fraction", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1996, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Fraction of strain rate that is plastic: 1 - \u03b7_vep / \u03b7_ve.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": true + }, + { + "name": "is_elastic", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2001, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Returns various norms on the mesh for the provided function.\n - size\n - mean\n - min\n - max\n - sum\n - L2 norm\n - rms\n\n NOTE: this currently assumes scalar variables !", + "existing_docstring": "True if elastic behavior is active (finite dt_elastic and shear_modulus).", "harvested_comments": [ - "This uses a private work MeshVariable and the various norms defined there but", - "could either be simplified to just use petsc vectors, or extended to", - "compute integrals over the elements which is in line with uw1 and uw2" + "If any of these is not defined, elasticity is switched off" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "meshVariable_mask_from_label", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2863, - "signature": "(self, label_name, label_value)", - "parameters": [ - { - "name": "label_name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "label_value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "is_viscoplastic", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2014, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Extract single label value and make a point mask - note: this produces a mask on the mesh points and\nassumes a 1st order mesh. Cell labels are not respected in this function.", + "existing_docstring": "True if plastic yielding is active (finite yield_stress).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "ViscoElasticPlasticFlowModel", "is_public": true }, { - "name": "register_swarm", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2889, - "signature": "(self, swarm)", - "parameters": [ - { - "name": "swarm", - "type_hint": null, - "default": null, - "description": "" - } + "name": "K", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2110, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Diffusivity :math:`\\kappa` (alias for ``diffusivity``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "DiffusionModel", + "is_public": true + }, + { + "name": "diffusivity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2115, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Register swarm as dependent on this mesh for coordinate change notifications", + "existing_docstring": "Scalar or tensor diffusivity :math:`\\kappa`.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "DiffusionModel", "is_public": true }, { - "name": "unregister_swarm", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2893, - "signature": "(self, swarm)", + "name": "diffusivity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2184, + "signature": "(inner_self)", "parameters": [ { - "name": "swarm", + "name": "inner_self", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Unregister swarm (called during swarm cleanup)", - "harvested_comments": [ - "WeakSet handles weak references internally, just remove the swarm directly" - ], + "existing_docstring": "Diagonal diffusivity tensor.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "register_surface", + "name": "diffusivity", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2898, - "signature": "(self, surface)", + "file": "src/underworld3/constitutive_models.py", + "line": 2189, + "signature": "(inner_self, value: sympy.Matrix)", "parameters": [ { - "name": "surface", + "name": "inner_self", "type_hint": null, "default": null, "description": "" + }, + { + "name": "value", + "type_hint": "sympy.Matrix", + "default": null, + "description": "" } ], "returns": null, - "existing_docstring": "Register surface as dependent on this mesh for adaptation notifications.", - "harvested_comments": [], + "existing_docstring": "Set diffusivity from a vector of per-direction values.", + "harvested_comments": [ + "Accept shape (dim, 1) or (1, dim)", + "Validate each component using validate_parameters", + "Store the validated diffusivity as a diagonal matrix" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "unregister_surface", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2902, - "signature": "(self, surface)", + "name": "flux", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2259, + "signature": "(inner_self)", "parameters": [ { - "name": "surface", + "name": "inner_self", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Unregister surface (called during surface cleanup).", + "existing_docstring": "User-defined flux expression.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "adapt", + "name": "flux", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2917, - "signature": "(self, metric_field, verbose = False)", + "file": "src/underworld3/constitutive_models.py", + "line": 2264, + "signature": "(inner_self, value: sympy.Matrix)", "parameters": [ { - "name": "metric_field", + "name": "inner_self", "type_hint": null, "default": null, "description": "" }, { - "name": "verbose", - "type_hint": null, - "default": "False", + "name": "value", + "type_hint": "sympy.Matrix", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Adapt the mesh discretization based on a metric field.\n\nThis method refines or coarsens the mesh in place, automatically\ntransferring all attached MeshVariables, updating Surfaces, and\nmarking Solvers for rebuild on their next solve() call.\n\nParameters\n----------\nmetric_field : MeshVariable\n A scalar MeshVariable containing target edge lengths (H field).\n Smaller values mean finer mesh, larger values mean coarser.\nverbose : bool, optional\n If True, print progress and statistics during adaptation.\n\nNotes\n-----\nThe adaptation uses PETSc's mesh adaptation with MMG/pragmatic backend.\n\n**What happens automatically:**\n\n- MeshVariables are interpolated to the new mesh\n- Surfaces recompute their distance fields\n- Swarms are marked as stale (particle-element associations invalidated)\n- Solvers are marked for rebuild (happens lazily on next solve())\n\nExamples\n--------\n>>> # Define metric from fault distance\n>>> metric = uw.discretisation.MeshVariable(\"H\", mesh, 1)\n>>> with mesh.access(metric):\n... # Smaller H near fault, larger far away\n... metric.data[:, 0] = 0.01 + 0.09 * fault.distance_from(mesh.data)\n>>> mesh.adapt(metric, verbose=True)\n>>> stokes.solve() # Solver rebuilds automatically", + "existing_docstring": "Set the flux expression (must be a vector of length dim).", "harvested_comments": [ - "Define metric from fault distance", - "Smaller H near fault, larger far away", - "Solver rebuilds automatically", - "Store old state for transfer", - "Notify surfaces to mark their distance fields as stale" + "Accept shape (dim, 1) or (1, dim)", + "Flatten and validate" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "_Parameters", "is_public": true }, { - "name": "meshVariable_lookup_by_symbol", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 3342, - "signature": "(mesh, sympy_object)", + "name": "flux", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2288, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "The user-defined flux expression.", + "harvested_comments": [ + "if self._flux is None:", + "raise RuntimeError(\"Flux expression has not been set.\")" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "GenericFluxModel", + "is_public": true + }, + { + "name": "s", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2383, + "signature": "(inner_self)", "parameters": [ { - "name": "mesh", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "sympy_object", + "name": "inner_self", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Given a sympy object, scan the mesh variables in `mesh` to find the\nlocation (meshvariable, component in the data array) corresponding to the symbol\nor return None if not found", + "existing_docstring": "Body force vector (e.g., gravitational source term :math:`\\rho \\mathbf{g}`).", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_Parameters", "is_public": true }, { - "name": "petsc_dm_find_labeled_points_local", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 3359, - "signature": "(dm, label_name, label_value, sectionIndex = False, verbose = False)", + "name": "s", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2388, + "signature": "(inner_self, value: sympy.Matrix)", "parameters": [ { - "name": "dm", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "label_name", + "name": "inner_self", "type_hint": null, "default": null, "description": "" }, { - "name": "label_value", - "type_hint": null, + "name": "value", + "type_hint": "sympy.Matrix", "default": null, "description": "" - }, - { - "name": "sectionIndex", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Identify local points associated with \"Label\"\n\ndm -> expects a petscDM object\nlabel_name -> \"String Name for Label\"\nsectionIndex -> False: leave points as indexed by the relevant section on the dm\n True: index into the local coordinate array\n\nNOTE: Assumes uniform element types", + "existing_docstring": "Set the body force vector.", "harvested_comments": [ - "print(f\"Label: {label_name} / {label_value}\")", - "print(f\"points: {pStart}: {pEnd}\")", - "print(f\"edges : {eStart}: {eEnd}\")", - "print(f\"faces : {fStart}: {fEnd}\")", - "print(f\"\", flush=True)" + "Update expression content in-place to preserve object identity", + "Cannot use validate_parameters() as it doesn't handle matrices", + "UWexpression.sym setter handles sympy.Matrix directly" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_Parameters", "is_public": true }, { - "name": "units", + "name": "K", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 428, + "file": "src/underworld3/constitutive_models.py", + "line": 2397, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Return the units associated with this variable.", + "existing_docstring": "Permeability :math:`\\kappa` [m\u00b2] - the primary constitutive parameter.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "DarcyFlowModel", "is_public": true }, { - "name": "units", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 433, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "flux", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2440, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Set the units for this variable.", + "existing_docstring": "Computes the effect of the constitutive tensor on the gradients of the unknowns.\n(always uses the `c` form of the tensor). In general cases, the history of the gradients\nmay be required to evaluate the flux.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "DarcyFlowModel", "is_public": true }, { - "name": "has_units", + "name": "viscosity", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 438, + "file": "src/underworld3/constitutive_models.py", + "line": 2545, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Check if this variable has units.", + "existing_docstring": "Whatever the consistutive model defines as the effective value of viscosity\nin the form of an uw.expression", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicFlowModel", "is_public": true }, { - "name": "dimensionality", + "name": "K", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 443, + "file": "src/underworld3/constitutive_models.py", + "line": 2552, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Get the dimensionality of this variable.", - "harvested_comments": [ - "Use Pint directly to get dimensionality" - ], + "existing_docstring": "Whatever the consistutive model defines as the effective value of viscosity\nin the form of an uw.expression", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicFlowModel", "is_public": true }, { - "name": "clone", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 609, - "signature": "(self, name, varsymbol)", - "parameters": [ - { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "varsymbol", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "grad_u", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2559, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Create a copy of this variable with new name and symbol.\n\nCreates a new mesh variable with the same mesh, shape, type,\ndegree, and continuity as this variable, but with a different\nname and symbolic representation.\n\nParameters\n----------\nname : str\n Name for the new variable.\nvarsymbol : str\n LaTeX symbol for the new variable.\n\nReturns\n-------\nMeshVariable\n New mesh variable with copied structure but independent data.", + "existing_docstring": "Symmetric strain rate tensor (with 1/2 factor).\n\n.. math::\n \\dot{\\varepsilon}_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "_BaseMeshVariable", - "is_public": true - }, - { - "name": "pack_raw_data_to_petsc", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 640, - "signature": "(self, data_array, sync = True)", - "parameters": [ - { - "name": "data_array", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "sync", - "type_hint": null, - "default": "True", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Pack data array to PETSc using traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\ndata_array : numpy.ndarray\n Array data in traditional flat format (-1, num_components)\nsync : bool\n Whether to sync parallel operations (default True)", - "harvested_comments": [ - "Convert to expected shape: (-1, num_components)", - "Direct PETSc access (following mesh.access pattern)", - "Ensure vector is available", - "Mark mesh DM as initialized (replaces old _accessed flag logic)", - "Direct assignment to PETSc vec (like mesh.access does at line 1156)" - ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicFlowModel", "is_public": true }, { - "name": "pack_uw_data_to_petsc", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 694, - "signature": "(self, data_array, sync = True)", + "name": "dt_elastic", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2840, + "signature": "(inner_self)", "parameters": [ { - "name": "data_array", + "name": "inner_self", "type_hint": null, "default": null, "description": "" - }, - { - "name": "sync", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, - "existing_docstring": "Enhanced pack method that directly accesses mesh data without access() context.\nDesigned for the new meshVariable.array interface.\n\nCRITICAL: This is the entry point where dimensional data enters PETSc storage.\nAll unit conversion and non-dimensionalization must happen here.\n\nParameters\n----------\ndata_array : numpy.ndarray or UWQuantity or UnitAwareArray\n Array data to pack into mesh field. Can have units (will be converted).\nsync : bool\n Whether to sync parallel operations (default True)", - "harvested_comments": [ - "STEP 1: Handle unit conversion for incoming data", - "This is CRITICAL for the array setter to work correctly with units", - "Check if data has units (UWQuantity or UnitAwareArray)", - "Data has units - need to convert and extract magnitude", - "Convert incoming units to variable's units" - ], - "status": "partial", + "existing_docstring": "Timestep for VE formulas. Set by the solver.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "_Parameters", "is_public": true }, { - "name": "unpack_raw_data_from_petsc", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 797, - "signature": "(self, squeeze = True, sync = True)", + "name": "ve_effective_viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2887, + "signature": "(inner_self)", "parameters": [ { - "name": "squeeze", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "sync", + "name": "inner_self", "type_hint": null, - "default": "True", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Unpack data from PETSc in traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\nsqueeze : bool\n Whether to remove singleton dimensions (default True)\nsync : bool\n Whether to sync parallel operations (default True)\n\nReturns\n-------\nnumpy.ndarray\n Array data in traditional flat format (-1, num_components)", - "harvested_comments": [ - "Direct PETSc access (following mesh.access pattern at line 1156)", - "Ensure vector is available", - "Mark mesh DM as initialized (replaces old _accessed flag logic)", - "Get data directly from PETSc vec (like mesh.access does)", - "Sync parallel operations if requested" + "existing_docstring": "VE effective viscosity using \u03b7\u2081 (fault-plane viscosity).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "_BaseMeshVariable", + "parent_class": "_Parameters", "is_public": true }, { - "name": "unpack_uw_data_from_petsc", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 846, - "signature": "(self, squeeze = True, sync = True)", + "name": "t_relax", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2903, + "signature": "(inner_self)", "parameters": [ { - "name": "squeeze", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "sync", + "name": "inner_self", "type_hint": null, - "default": "True", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Enhanced unpack method that directly accesses mesh data without access() context.\nDesigned for the new meshVariable.array interface.\n\nParameters\n----------\nsqueeze : bool\n Whether to remove singleton dimensions (default True)\nsync : bool\n Whether to sync parallel operations (default True)\n\nReturns\n-------\nnumpy.ndarray\n Array data in correct shape for the variable", - "harvested_comments": [ - "Direct PETSc access (following mesh.access pattern at line 1156)", - "Ensure vector is available", - "Mark mesh DM as initialized (replaces old _accessed flag logic)", - "Get data directly from PETSc vec (like mesh.access does)", - "Unpack data using same layout as original method" + "existing_docstring": "Maxwell relaxation time: \u03b7\u2081 / \u03bc.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "_BaseMeshVariable", + "parent_class": "_Parameters", "is_public": true }, { - "name": "rbf_interpolate", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 905, - "signature": "(self, new_coords, meth = 0, p = 2, verbose = False, nnn = None, rubbish = None)", - "parameters": [ - { - "name": "new_coords", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "meth", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "p", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "nnn", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "rubbish", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "name": "is_elastic", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2911, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Interpolate variable data to new coordinates using RBF.\n\nUses inverse distance weighting with k-nearest neighbors to\ninterpolate values from mesh nodes to arbitrary coordinates.\n\nParameters\n----------\nnew_coords : numpy.ndarray\n Target coordinates of shape ``(n_points, dim)``.\nmeth : int, optional\n Interpolation method (reserved, currently unused).\np : float, optional\n Power parameter for inverse distance weighting (default: 2).\nverbose : bool, optional\n Print progress information.\nnnn : int, optional\n Number of nearest neighbors (default: 4 for 3D, 3 for 2D).\n\nReturns\n-------\nnumpy.ndarray\n Interpolated values at new coordinates.", - "harvested_comments": [ - "An inverse-distance mapping is quite robust here ... as long", - "as long we take care of the case where some nodes coincide (likely if used mesh2mesh)", - "Use non-dimensional coordinates for internal RBF interpolation KDTree" + "existing_docstring": "True if elastic behavior is active (finite shear_modulus).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "save", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 953, - "signature": "(self, filename: str, name: Optional[str] = None, index: Optional[int] = None)", - "parameters": [ - { - "name": "filename", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "name", - "type_hint": "Optional[str]", - "default": "None", - "description": "" - }, - { - "name": "index", - "type_hint": "Optional[int]", - "default": "None", - "description": "" - } - ], + "name": "is_viscoplastic", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2918, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Append variable data to the specified mesh hdf5\ndata file. The file must already exist.\n\nParameters\n----------\nfilename :\n The filename of the mesh checkpoint file. It\n must already exist.\nname :\n Textual name for dataset. In particular, this\n will be used for XDMF generation. If not\n provided, the variable name will be used.\nindex :\n Not currently supported. An optional index which\n might correspond to the timestep (for example).", - "harvested_comments": [ - "Keep vector available for future access", - "# JM:To enable timestep recording, the following needs to be called.", - "# I'm unsure if the corresponding xdmf functionality is enabled via", - "# the PETSc xdmf script.", - "PetscViewerHDF5PushTimestepping(cviewer)" - ], - "status": "partial", + "existing_docstring": "True if plastic yielding is active (finite yield_stress).", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "write", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1031, - "signature": "(self, filename: str)", - "parameters": [ - { - "name": "filename", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "name": "order", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 2925, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Write variable data to the specified mesh hdf5\ndata file. The file will be over-written.\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.\n\nParameters\n----------\nfilename :\n The filename of the mesh checkpoint file", - "harvested_comments": [ - "Keep vector available for future access", - "Variable coordinates - let's put those in the file to", - "make it a standalone \"swarm\"", - "# Add variable unit metadata to standalone file", - "Use preferred selective_ranks pattern for unit metadata" - ], - "status": "partial", + "existing_docstring": "Time integration order (1 or 2).", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "read_timestep", + "name": "order", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1125, - "signature": "(self, data_filename, data_name, index, outputPath = '', verbose = False)", + "file": "src/underworld3/constitutive_models.py", + "line": 2930, + "signature": "(self, value)", "parameters": [ { - "name": "data_filename", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "data_name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "index", + "name": "value", "type_hint": null, "default": null, "description": "" - }, - { - "name": "outputPath", - "type_hint": null, - "default": "''", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Read a mesh variable from an arbitrary vertex-based checkpoint file\nand reconstruct/interpolate the data field accordingly. The data sizes / meshes can be\ndifferent and will be matched using a kd-tree / inverse-distance weighting\nto the new mesh.", - "harvested_comments": [ - "Fix this to match the write_timestep function", - "mesh.write_timestep( \"test\", meshUpdates=False, meshVars=[X], outputPath=\"\", index=0)", - "swarm.write_timestep(\"test\", \"swarm\", swarmVars=[var], outputPath=\"\", index=0)", - "check if data_file exists", - "Keep vector available for future access" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "_BaseMeshVariable", - "is_public": true - }, - { - "name": "fn", - "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1247, - "signature": "(self) -> sympy.Basic", - "parameters": [], - "returns": "sympy.Basic", - "existing_docstring": "The handle to the (i,j,k) spatial view of this variable if it exists (deprecated)", + "existing_docstring": "Set time integration order (warns if DFDt already created).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "ijk", + "name": "effective_order", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1254, - "signature": "(self) -> sympy.Basic", + "file": "src/underworld3/constitutive_models.py", + "line": 2949, + "signature": "(self)", "parameters": [], - "returns": "sympy.Basic", - "existing_docstring": "The handle to the (i,j,k) spatial view of this variable if it exists", + "returns": null, + "existing_docstring": "Effective order accounting for DDt history startup.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "sym", + "name": "stress_history_ddt_kwargs", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1261, - "signature": "(self) -> sympy.Basic", + "file": "src/underworld3/constitutive_models.py", + "line": 3018, + "signature": "(self)", "parameters": [], - "returns": "sympy.Basic", - "existing_docstring": "The handle to the sympy.Matrix view of this variable", - "harvested_comments": [ - "Note: Scaling is applied during unwrap(), not here" - ], + "returns": null, + "existing_docstring": "ETD-2 (order=2) and hybrid need the forcing-history slot;\nBDF and ETD-1 do not.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "sym_1d", + "name": "bdf_blend", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1269, - "signature": "(self) -> sympy.Basic", + "file": "src/underworld3/constitutive_models.py", + "line": 3074, + "signature": "(self)", "parameters": [], - "returns": "sympy.Basic", - "existing_docstring": "The handle to a flattened version of the sympy.Matrix view of this variable.\nAssume components are stored in the same order that sympy iterates entries in\na matrix except for the symmetric tensor case where we store in a Voigt form", + "returns": null, + "existing_docstring": "BDF coefficient blending \u03b1 \u2208 [0, 1]. **Damping knob.**\n\nLinearly mixes BDF-1 and the requested-order coefficients:\n``c = (1-\u03b1)\u00b7c_BDF1 + \u03b1\u00b7c_requested_order``. \u03b1=1 is no blend.\n\n**For TI-VEP fault simulations, prefer ``order=1`` over tuning\nthis knob.** At ``order=2`` with a spatially varying\n``yield_stress`` field, the simulation drifts unstably:\n|\u03c3_xy| \u2192 10\u2078 over ~10 t_r. Empirically the instability is\ngated by the magnitude of the \u03c8*_{n-1} weight: \u03b1 \u2264 0.25 stable,\n\u03b1 \u2265 0.5 blow-up. Lower \u03b1 values stabilise but throw away\nmost of BDF-2's accuracy advantage \u2014 at \u03b1=0.10 the trace\ndifference vs ``order=1`` is ~0.1% of peak, for ~50% wall-time\noverhead. Use this knob only if you specifically need\norder-2 behaviour on a problem with uniform yield_stress.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "mesh", + "name": "stress_star", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1537, + "file": "src/underworld3/constitutive_models.py", + "line": 3100, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "The mesh this variable belongs to (accessed via weak reference).\nRaises RuntimeError if the mesh has been garbage collected.", + "existing_docstring": "Previous timestep stress from history.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "vec", + "name": "E_eff", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1554, - "signature": "(self) -> PETSc.Vec", + "file": "src/underworld3/constitutive_models.py", + "line": 3141, + "signature": "(self)", "parameters": [], - "returns": "PETSc.Vec", - "existing_docstring": "The corresponding PETSc local vector for this variable.", - "harvested_comments": [ - "Ensure vector is initialized when accessed" - ], - "status": "minimal", + "returns": null, + "existing_docstring": "Effective strain rate including elastic-history coupling.\n\nBDF: ``E_eff = \u03b5\u0307 - \u03a3c_i\u00b7\u03c3*/(2\u03bc\u0394t)``.\nETD-2: ``E_eff = (1-\u03c6)\u00b7\u03b5\u0307 + \u03b1\u00b7\u03c3*/(2\u03b7\u2081) + (\u03c6-\u03b1)\u00b7\u03b5\u0307*``.\nHybrid: returns the BDF form (E_eff is consumed by yield-clip\ncode that should see the BDF rate metric); the actual flux uses\nboth forms inside :py:meth:`stress`.", + "harvested_comments": [], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "old_data", + "name": "E_eff_inv_II", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1568, - "signature": "(self) -> numpy.ndarray", + "file": "src/underworld3/constitutive_models.py", + "line": 3155, + "signature": "(self)", "parameters": [], - "returns": "numpy.ndarray", - "existing_docstring": "TESTING: Original data property implementation.\nNumpy proxy array to underlying variable data.\nNote that the returned array is a proxy for all the *local* nodal\ndata, and is provided as 1d list.\n\nFor both read and write, this array can only be accessed via the\nmesh `access()` context manager.", + "returns": null, + "existing_docstring": "Second invariant of effective strain rate.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "array", + "name": "viscosity", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1583, + "file": "src/underworld3/constitutive_models.py", + "line": 3162, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Array view of canonical data with automatic format conversion.\nShape: (N, a, b) for tensor shape (a, b).\n\nThis property is ALWAYS a view of the canonical .data property.\nNo direct PETSc access - all changes delegate back to canonical storage.", - "harvested_comments": [], + "existing_docstring": "Effective viscosity for the fault-plane shear component.\n\nApplies the yield mode (softmin/min/harmonic) to \u03b7\u2081, leaving\n\u03b7\u2080 (bulk) unchanged. The anisotropic tensor handles the\ndirectional dependence.", + "harvested_comments": [ + "\u03b7\u2081 is the fault-plane viscosity that gets yield-limited", + "float offset avoids sympy expression blowup in tensor" + ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "data", + "name": "K", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2253, + "file": "src/underworld3/constitutive_models.py", + "line": 3194, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Canonical data storage with PETSc synchronization.\nShape: (-1, num_components) - flat format for backward compatibility.\n\nThis is the ONLY property that handles PETSc synchronization to avoid conflicts.\nThe .array property uses this as its underlying storage with format conversion.\n\nReturns\n-------\nNDArray_With_Callback\n Array with shape (-1, num_components) with automatic PETSc synchronization", - "harvested_comments": [ - "Cache and reuse canonical data object to avoid field access conflicts", - "Use direct __dict__ check to avoid potential attribute access issues", - "Create the single canonical data array with PETSc sync" - ], - "status": "partial", + "existing_docstring": "Effective stiffness for preconditioner.", + "harvested_comments": [], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "array", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2341, - "signature": "(self, array_value)", - "parameters": [ - { - "name": "array_value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "flux", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3346, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Set variable data using pack method to handle shape transformation.", - "harvested_comments": [ - "Use pack method to handle proper data transformation and shape conversion" - ], + "existing_docstring": "Stress flux for the weak form.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "min", + "name": "stress_projection", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2395, - "signature": "(self) -> Union[float, tuple]", + "file": "src/underworld3/constitutive_models.py", + "line": 3350, + "signature": "(self)", "parameters": [], - "returns": "Union[float, tuple]", - "existing_docstring": "The global variable minimum value.\nReturns the value only (not the rank). For multi-component variables,\nreturns a tuple of minimum values for each component.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", + "returns": null, + "existing_docstring": "VE stress without plastic correction (for history storage).\n\nUses the anisotropic tensor with VE effective viscosities but\nno yield limiting (\u03b7\u2081_ve, not \u03b7\u2081_eff). This is the stress that\nshould be stored in the DFDt history for the next timestep.", "harvested_comments": [ - "Sync local\u2192global to ensure global vector has latest data", - "Get raw non-dimensional values from PETSc", - "Dimensionalise using units system" + "Contract with the VE-only tensor (not self._c which has yield)" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "max", + "name": "stress", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2423, - "signature": "(self) -> Union[float, tuple]", + "file": "src/underworld3/constitutive_models.py", + "line": 3402, + "signature": "(self)", "parameters": [], - "returns": "Union[float, tuple]", - "existing_docstring": "The global variable maximum value.\nReturns the value only (not the rank). For multi-component variables,\nreturns a tuple of maximum values for each component.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", + "returns": null, + "existing_docstring": "Viscoelastic-plastic anisotropic stress for the weak form.\n\nMatches isotropic VEP pattern: tensor contraction for current strain\nrate, scalar yield-limited viscosity for VE history terms. The tensor\nC(\u03b7\u2081_eff) handles anisotropy; the history uses the same \u03b7\u2081_eff as\na scalar multiplier (consistent with how isotropic VEP uses\nself.viscosity for both).\n\nHybrid path: ``\u03c3 = w\u00b7\u03c3_BDF + (1-w)\u00b7\u03c3_ETD`` with the spatial\nweight from ``self._fault_weight``. Each branch contracts its\nown E_eff with its own C-tensor; the blend lives at the flux\nlevel so neither integrator's structure is compromised.", "harvested_comments": [ - "Sync local\u2192global to ensure global vector has latest data", - "Get raw non-dimensional values from PETSc", - "Dimensionalise using units system" + "\u03c3_BDF: BDF flux with yield-clipped C tensor", + "\u03c3_ETD: ETD flux with no-yield C tensor", + "Spatial blend", + "Apply the anisotropic tensor to the effective strain rate", + "(current + VE history): \u03c3 = C(\u03b7\u2080_ve, \u03b7\u2081_eff) : \u03b5\u0307_eff" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "sum", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2451, - "signature": "(self) -> Union[float, tuple]", + "name": "yield_mode", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3454, + "signature": "(self)", "parameters": [], - "returns": "Union[float, tuple]", - "existing_docstring": "The global variable sum value.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", - "harvested_comments": [ - "Sync local\u2192global to ensure global vector has latest data", - "Get raw non-dimensional values from PETSc", - "Dimensionalise using units system" - ], + "returns": null, + "existing_docstring": "How to apply yield limiting to the fault-plane viscosity.\n\nSame options as :class:`ViscoElasticPlasticFlowModel`:\n``\"softmin\"`` (default), ``\"harmonic\"``, ``\"min\"``. The\n``\"smooth\"`` option was retired (under-clipped by ~50 %); see\nthe parent class's :attr:`yield_mode` docstring for details.", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "norm", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2479, - "signature": "(self, norm_type) -> Union[float, tuple]", - "parameters": [ - { - "name": "norm_type", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": "Union[float, tuple]", - "existing_docstring": "The global variable norm value.\n\nnorm_type: type of norm, one of\n - 0: NORM 1 ||v|| = sum_i | v_i |. ||A|| = max_j || v_*j ||\n - 1: NORM 2 ||v|| = sqrt(sum_i |v_i|^2) (vectors only)\n - 3: NORM INFINITY ||v|| = max_i |v_i|. ||A|| = max_i || v_i* ||, maximum row sum\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", - "harvested_comments": [ - "Sync local\u2192global to ensure global vector has latest data", - "Get raw non-dimensional values from PETSc", - "Dimensionalise using units system" - ], - "status": "partial", + "name": "yield_softness", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3480, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Regularisation parameter \u03b4 for softmin mode.", + "harvested_comments": [], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "mean", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2512, - "signature": "(self) -> Union[float, tuple]", + "name": "requires_stress_history", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3490, + "signature": "(self)", "parameters": [], - "returns": "Union[float, tuple]", - "existing_docstring": "The global variable mean value.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", - "harvested_comments": [ - "Sync local\u2192global to ensure global vector has latest data", - "Get raw non-dimensional values from PETSc", - "Dimensionalise using units system" - ], - "status": "partial", + "returns": null, + "existing_docstring": "Transverse isotropic VEP requires stress history tracking.", + "harvested_comments": [], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "std", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2539, - "signature": "(self) -> Union[float, tuple]", + "name": "plastic_fraction", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3495, + "signature": "(self)", "parameters": [], - "returns": "Union[float, tuple]", - "existing_docstring": "The global variable standard deviation value.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", + "returns": null, + "existing_docstring": "Fraction of strain rate that is plastic.", "harvested_comments": [ - "Sync local\u2192global to ensure global vector has latest data", - "Get raw values from PETSc", - "For scalar: std = sqrt((sum(x^2)/n) - (sum(x)/n)^2)", - "Create a temporary vector for x^2 computation", - "Calculate variance: E[x^2] - (E[x])^2" + "viscosity property returns \u03b7\u2080, need to compare \u03b7\u2081 effective vs \u03b7\u2081 ve", + "This is approximate for the anisotropic case" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPFlowModel", "is_public": true }, { - "name": "stats", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2595, + "name": "tau_par_cap_factor", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3602, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Universal statistics method for all variable types.\n\nReturns various statistical measures appropriate for the variable type.\nFor scalars: standard statistical measures.\nFor vectors: magnitude-based statistics.\nFor tensors: Frobenius norm and invariant-based measures.\n\nReturns\n-------\ndict\n Dictionary containing statistical measures:\n - 'type': Variable type ('scalar', 'vector', 'tensor')\n - 'components': Number of components\n - 'size': Number of elements\n - 'mean': Mean value (scalar) or magnitude mean (vector/tensor)\n - 'min': Minimum value (scalar) or magnitude min (vector/tensor)\n - 'max': Maximum value (scalar) or magnitude max (vector/tensor)\n - 'sum': Sum of all values\n - 'norm2': L2 norm\n - 'rms': Root mean square\n\n Additional keys for vectors/tensors:\n - 'magnitude_*': Statistics on vector magnitude\n - 'frobenius_*': Statistics on tensor Frobenius norm (for tensors)\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.", - "harvested_comments": [ - "This uses a private work MeshVariable and the various norms defined there but", - "could either be simplified to just use petsc vectors, or extended to", - "compute integrals over the elements which is in line with uw1 and uw2" - ], + "existing_docstring": "Lower bound ``c`` such that ``\u03c4_\u2225 \u2265 c\u00b7\u0394t`` in the parallel\nbranch's exponential factor.\n\nCaps how aggressively the parallel-branch's elastic memory is\nrelaxed during yielding. Without this cap, ``\u03b7_\u2225_eff \u2192 0`` at\ndeep yield drives ``\u03b1_\u2225 = exp(-\u0394t/\u03c4_\u2225) \u2192 0`` \u2014 boundary\nmotion goes straight into slip each step with no elastic\nspring-back, ratcheting fault displacement at the BC rate.\nWith ``c=1`` (default), ``\u03b1_\u2225 \u2265 1/e``; with ``c=2``,\n``\u03b1_\u2225 \u2265 exp(-0.5)``.\n\nSet to ``0`` to disable (recovers the explicit-plasticity\nbehaviour). The cap applies only to the (\u03b1_\u2225, \u03c6_\u2225) factors \u2014\n``C_\u2225`` keeps the natural yield-clipped \u03b7_\u2225 so ``\u03c3_\u2225`` still\nsits at the yield surface.", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPSplitFlowModel", "is_public": true }, { - "name": "coords", - "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2761, - "signature": "(self) -> numpy.ndarray", + "name": "stress", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3792, + "signature": "(self)", "parameters": [], - "returns": "numpy.ndarray", - "existing_docstring": "The array of variable vertex coordinates for this variable's DOF locations.\n\nReturns coordinates for this variable's specific degree-of-freedom locations,\nwhich may differ from mesh coordinate variable locations if the degrees differ.\n\nWhen mesh has reference quantities set, returns unit-aware coordinates in meters.", + "returns": null, + "existing_docstring": "Per-component ETD-2 flux with **lagged** ``(\u03b1_\u2225, \u03c6_\u2225)``.\n\nEach branch's E_eff is built and contracted with its own\nsub-modulus; the two are summed.\n\n``\u03b1_\u2225, \u03c6_\u2225`` are derived from a *lagged* parallel viscosity\ncomputed from the projected stress and strain-rate histories:\n\n.. math::\n \\eta_\\parallel^{\\,\\mathrm{lag}}\n = \\frac{|\\sigma^*_\\parallel|}\n {2\\,\\max(|\\dot\\varepsilon^*_\\parallel|, \\dot\\varepsilon_\\min)}\n\nwhere ``|\u00b7|_\u2225`` is the Pythagorean fault-plane shear magnitude\n(same pattern as :py:meth:`_plastic_effective_viscosity`). This\nsits naturally on the yield surface during yielding (because\n``|\u03c3^*_\u2225|`` saturates near ``\u03c4_y`` while ``|\u03b5\u0307^*_\u2225|`` is large)\nand tracks the elastic VE response otherwise. Critically the\nexpression depends only on previous-step storage \u2014 no\nplasticity-clip recursion through ``E_eff`` \u2014 keeping the JIT\ncodegen tree shallow.\n\nThe C_\u2225 sub-modulus still uses the **current** yield-clipped\n``\u03b7_\u2225_eff`` (preserves Newton's nonlinear plasticity Jacobian\nthrough the multiplicative response weights).", "harvested_comments": [ - "Get non-dimensional [0-1] model coordinates for this variable's specific DOF locations", - "If mesh has units, dimensionalise to physical coordinates", - "Dimensionalise using the proper units system", - "Specify length dimensionality since coords have dimension [length]", - "No units - return non-dimensional coordinates" + "Matrix branch: scalar (\u03b1_\u22a5, \u03c6_\u22a5) from DDt", + "\u2500\u2500 Lagged \u03b7_\u2225_eff via parent's softmin envelope, evaluated", + "against forcing_star (previous-step \u03b5\u0307) instead of E_eff", + "(current Newton iterate). Breaks the per-quad-split's", + "1-iteration trivial-Newton failure mode." ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "TransverseIsotropicVEPSplitFlowModel", "is_public": true }, { - "name": "coords_nd", + "name": "flux", "kind": "property", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2791, - "signature": "(self) -> numpy.ndarray", + "file": "src/underworld3/constitutive_models.py", + "line": 4012, + "signature": "(self)", "parameters": [], - "returns": "numpy.ndarray", - "existing_docstring": "Non-dimensional [0-1] coordinates for this variable's DOF locations.\n\nReturns raw model coordinates from PETSc without any unit wrapping.\nThis is the coordinate system used by internal KDTree indexing, evaluation,\nand other algorithmic operations.\n\nFor user-facing operations with physical units, use `.coords` which returns\ndimensional coordinates when the model has reference quantities set.\n\nReturns\n-------\nndarray\n Non-dimensional [0-1] coordinates, shape (N, dim)\n\nExamples\n--------\n>>> # Internal algorithmic use - KDTree indexing\n>>> kd_tree = uw.kdtree.KDTree(var.coords_nd)\n>>>\n>>> # User-facing display with dimensional units\n>>> print(f\"Positions: {var.coords}\") # Shows meters, km, etc.\n\nNotes\n-----\nThis is a zero-copy operation that returns a view of the cached coordinate\narray directly from the mesh. No memory allocation or copying occurs.\n\nSee Also\n--------\ncoords : Dimensional coordinates with unit wrapping (user-facing)", + "returns": null, + "existing_docstring": "Compute level-set weighted average of constituent model fluxes.\n\nCRITICAL: This composite flux becomes the stress history that\nall constituent models (including elastic ones) will read via\n``DFDt.psi_star[0]`` in the next time step.", "harvested_comments": [ - "Internal algorithmic use - KDTree indexing", - "User-facing display with dimensional units", - "Shows meters, km, etc.", - "Direct access to non-dimensional coordinates from mesh cache", - "This is a ZERO-COPY operation - returns cached array directly" + "Get reference flux shape from first model", + "Compute normalization factor to ensure partition of unity", + "Get normalized level-set function for material i", + "Get flux contribution from constituent model i", + "Add weighted contribution to composite flux" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "MultiMaterialConstitutiveModel", "is_public": true }, { - "name": "divergence", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2831, + "name": "K", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 4052, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Divergence of this variable: :math:`\\nabla \\cdot \\mathbf{v}`.\n\nUses the mesh's coordinate-aware vector calculus operators,\nwhich correctly handle cylindrical, spherical, or other\nnon-Cartesian coordinate systems.\n\nReturns\n-------\nsympy.Expr or None\n Scalar divergence expression, or None if the operation fails.", - "harvested_comments": [], + "existing_docstring": "Effective stiffness using level-set weighted harmonic average.\n\nFor composite materials, harmonic averaging gives the correct effective\nstiffness for preconditioning: $1/K_{eff} = \\sum(\\phi_i / K_i) / \\sum(\\phi_i)$", + "harvested_comments": [ + "Harmonic average: 1/K_eff = sum(phi_i / K_i) / sum(phi_i)", + "Compute normalization factor to ensure partition of unity", + "Get normalized level-set function for material i", + "Get stiffness from constituent model i", + "Add weighted contribution to inverse stiffness" + ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "MultiMaterialConstitutiveModel", "is_public": true }, { - "name": "gradient", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2848, + "name": "sym", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 166, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Gradient of this variable: :math:`\\nabla u`.\n\nUses the mesh's coordinate-aware vector calculus operators.\n\nReturns\n-------\nsympy.Matrix or None\n Gradient vector as row matrix, or None if the operation fails.", + "existing_docstring": "Symbolic representation for JIT/symbolic operations.\n\nFor UWCoordinate (which IS a BaseScalar), this returns self.\nThis maintains API compatibility with code that accesses .sym", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "UWCoordinate", "is_public": true }, { - "name": "curl", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2863, + "name": "data", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 176, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Curl of this variable: :math:`\\nabla \\times \\mathbf{v}`.\n\nUses the mesh's coordinate-aware vector calculus operators.\n\nReturns\n-------\nsympy.Matrix, sympy.Expr, or None\n Curl vector (3D) or scalar vorticity (2D), or None if fails.", + "existing_docstring": "Coordinate values from mesh.\n\nReturns dimensional values if mesh has units, ND otherwise.\nMirrors MeshVariable.data pattern.\n\nReturns\n-------\nnumpy.ndarray\n Coordinate values for this axis at all mesh nodes", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "UWCoordinate", "is_public": true }, { - "name": "jacobian", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2878, + "name": "mesh", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 193, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Jacobian matrix of this variable.\n\nComputes partial derivatives with respect to mesh coordinates:\n:math:`J_{ij} = \\partial v_i / \\partial x_j`.\n\nReturns\n-------\nsympy.Matrix\n Jacobian matrix of shape (var_dim, mesh_dim).", + "existing_docstring": "Parent mesh.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "UWCoordinate", "is_public": true }, { - "name": "data", + "name": "axis", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 244, + "file": "src/underworld3/coordinates.py", + "line": 198, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Direct access to data array (supports += and other assignment ops).", + "existing_docstring": "Axis index (0=x, 1=y, 2=z).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "UWCoordinate", "is_public": true }, { - "name": "array", + "name": "units", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 249, + "file": "src/underworld3/coordinates.py", + "line": 208, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Direct access to array (delegates to base variable).\n\nThe base variable's array implementation now handles units and\nnon-dimensional scaling correctly, so we just delegate to it.\nThis eliminates code duplication and ensures consistency.", - "harvested_comments": [ - "Simply delegate to the base variable's array property", - "The base variable now has the complete, working implementation" - ], + "existing_docstring": "Units of this coordinate, delegated from the original BaseScalar.\n\nThe mesh's patch_coordinate_units() sets ._units on mesh.N.x, mesh.N.y, etc.\nUWCoordinate wraps these, so we delegate to get the units.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "UWCoordinate", "is_public": true }, { - "name": "array", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 262, - "signature": "(self, value)", + "name": "uwdiff", + "kind": "function", + "file": "src/underworld3/coordinates.py", + "line": 260, + "signature": "(expr, *symbols)", "parameters": [ { - "name": "value", + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "*symbols", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set array values.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Differentiate an expression with respect to coordinates.\n\n.. deprecated:: December 2025\n Since UWCoordinate now subclasses BaseScalar with proper __eq__/__hash__,\n you can use ``sympy.diff(expr, y)`` directly. This function is kept for\n backward compatibility but simply delegates to sympy.diff().\n\nParameters\n----------\nexpr : sympy.Expr\n The expression to differentiate\nsymbols : UWCoordinate or sympy.Symbol\n The variables to differentiate with respect to\n\nReturns\n-------\nsympy.Expr\n The derivative\n\nExamples\n--------\n>>> x, y = mesh.X # UWCoordinates\n>>> v = mesh_variable # MeshVariable\n>>> # Both now work identically:\n>>> dv_dy = sympy.diff(v.sym[0], y) # Preferred\n>>> dv_dy = uw.uwdiff(v.sym[0], y) # Backward compatible", + "harvested_comments": [ + "UWCoordinates", + "MeshVariable", + "Both now work identically:", + "Backward compatible" ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": true }, { - "name": "name", + "name": "geographic_to_cartesian", + "kind": "function", + "file": "src/underworld3/coordinates.py", + "line": 369, + "signature": "(lon_deg, lat_deg, depth_km, a, b)", + "parameters": [ + { + "name": "lon_deg", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "lat_deg", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "depth_km", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert geographic coordinates to Cartesian coordinates.\n\nUses geodetic latitude (perpendicular to ellipsoid surface).\n\nParameters\n----------\nlon_deg : float or array\n Longitude in degrees East (-180 to 180 or 0 to 360)\nlat_deg : float or array\n Latitude in degrees North (-90 to 90), geodetic\ndepth_km : float or array\n Depth below ellipsoid surface in km (positive downward)\na : float\n Semi-major axis (equatorial radius) in km\nb : float\n Semi-minor axis (polar radius) in km\n\nReturns\n-------\nx, y, z : float or array\n Cartesian coordinates in km", + "harvested_comments": [ + "Convert to radians", + "Eccentricity squared", + "Prime vertical radius of curvature at this latitude", + "Height above ellipsoid (negative of depth)", + "Cartesian coordinates" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "cartesian_to_geographic", + "kind": "function", + "file": "src/underworld3/coordinates.py", + "line": 414, + "signature": "(x, y, z, a, b, max_iterations = 10, tolerance = 1e-12)", + "parameters": [ + { + "name": "x", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "z", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "max_iterations", + "type_hint": null, + "default": "10", + "description": "" + }, + { + "name": "tolerance", + "type_hint": null, + "default": "1e-12", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert Cartesian coordinates to geographic coordinates.\n\nUses iterative algorithm (Bowring's method) for geodetic latitude.\n\nParameters\n----------\nx, y, z : float or array\n Cartesian coordinates in km\na : float\n Semi-major axis (equatorial radius) in km\nb : float\n Semi-minor axis (polar radius) in km\nmax_iterations : int, optional\n Maximum iterations for latitude convergence (default: 10)\ntolerance : float, optional\n Convergence tolerance in radians (default: 1e-12)\n\nReturns\n-------\nlon_deg, lat_deg, depth_km : float or array\n Geographic coordinates:\n - lon_deg: Longitude in degrees East\n - lat_deg: Latitude in degrees North (geodetic)\n - depth_km: Depth below ellipsoid surface in km", + "harvested_comments": [ + "Longitude is straightforward", + "Latitude requires iteration (Bowring's method for geodetic latitude)", + "Initial guess for latitude (geocentric)", + "Iterate to converge on geodetic latitude", + "Check convergence" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "lon", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 269, - "signature": "(self) -> str", + "file": "src/underworld3/coordinates.py", + "line": 561, + "signature": "(self)", "parameters": [], - "returns": "str", - "existing_docstring": "Variable name.", + "returns": null, + "existing_docstring": "Longitude in degrees East (-180 to 180).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "clean_name", + "name": "lat", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 274, - "signature": "(self) -> str", + "file": "src/underworld3/coordinates.py", + "line": 567, + "signature": "(self)", "parameters": [], - "returns": "str", - "existing_docstring": "Cleaned variable name for PETSc.", + "returns": null, + "existing_docstring": "Geodetic latitude in degrees North (-90 to 90).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "mesh", + "name": "depth", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 279, + "file": "src/underworld3/coordinates.py", + "line": 573, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "The mesh this variable is defined on.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Depth below ellipsoid surface (positive downward).\n\nReturns nondimensional values when units are active, km otherwise.\nUse with units system for proper dimensional output.", + "harvested_comments": [ + "Return with units if available", + "Check if units system is active for wrapping", + "Get reference length to dimensionalize", + "Depth in km = nondimensional * L_ref" + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { "name": "coords", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 287, + "file": "src/underworld3/coordinates.py", + "line": 600, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Coordinate array from base variable.", + "existing_docstring": "Geographic coordinates as (N, 3) array: [lon, lat, depth].\n\nReturns mesh node coordinates in geographic form, matching the\nlayout of `mesh.X.coords` but in (longitude, latitude, depth) format.\n\nReturns\n-------\nnumpy.ndarray\n Shape (N, 3) array where columns are:\n - Column 0: Longitude (degrees East, -180 to 180)\n - Column 1: Latitude (degrees North, -90 to 90)\n - Column 2: Depth (km below ellipsoid surface, positive down)\n\nExamples\n--------\n>>> geo_coords = mesh.CoordinateSystem.geo.coords\n>>> print(f\"Node 0: lon={geo_coords[0,0]:.2f}\u00b0, lat={geo_coords[0,1]:.2f}\u00b0\")\n\nSee Also\n--------\nmesh.X.coords : Cartesian coordinates (x, y, z)\nlon, lat, depth : Individual coordinate arrays", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "coords_nd", + "name": "unit_WE", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 292, + "file": "src/underworld3/coordinates.py", + "line": 644, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Non-dimensional coordinates from base variable (for internal KDTree operations).", + "existing_docstring": "West to East unit vector (primary name, positive East).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "num_components", + "name": "unit_SN", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 297, - "signature": "(self) -> int", + "file": "src/underworld3/coordinates.py", + "line": 649, + "signature": "(self)", "parameters": [], - "returns": "int", - "existing_docstring": "Number of components.", + "returns": null, + "existing_docstring": "South to North unit vector (primary name, positive North).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "shape", + "name": "unit_down", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 302, + "file": "src/underworld3/coordinates.py", + "line": 654, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Variable shape.", + "existing_docstring": "Downward unit vector (primary name, positive into planet).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "vtype", + "name": "unit_east", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 307, + "file": "src/underworld3/coordinates.py", + "line": 661, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Variable type.", + "existing_docstring": "Eastward unit vector (directional alias for unit_WE).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "degree", + "name": "unit_west", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 312, - "signature": "(self) -> int", + "file": "src/underworld3/coordinates.py", + "line": 666, + "signature": "(self)", "parameters": [], - "returns": "int", - "existing_docstring": "Polynomial degree.", + "returns": null, + "existing_docstring": "Westward unit vector (opposite of unit_WE).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "units", + "name": "unit_north", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 319, + "file": "src/underworld3/coordinates.py", + "line": 671, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Units for this variable.", + "existing_docstring": "Northward unit vector (directional alias for unit_SN).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "has_units", + "name": "unit_south", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 324, - "signature": "(self) -> bool", + "file": "src/underworld3/coordinates.py", + "line": 676, + "signature": "(self)", "parameters": [], - "returns": "bool", - "existing_docstring": "Check if this variable has units.", + "returns": null, + "existing_docstring": "Southward unit vector (opposite of unit_SN).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "dimensionality", + "name": "unit_up", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 329, + "file": "src/underworld3/coordinates.py", + "line": 681, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Get dimensionality (delegates to DimensionalityMixin or base variable).", - "harvested_comments": [ - "DimensionalityMixin.dimensionality uses self.units, which now delegates to _base_var", - "This ensures consistency" - ], + "existing_docstring": "Upward unit vector (opposite of unit_down).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "units_repr", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 335, + "name": "unit_lon", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 688, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Return string representation with units information.", + "existing_docstring": "Longitude direction unit vector (coordinate alias for unit_WE).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "non_dimensional_value", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 342, - "signature": "(self, model = None)", - "parameters": [ - { - "name": "model", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Get non-dimensionalized values of the variable.\n\nReturns the variable's data array in non-dimensional form based on\nthe model's reference quantities.", - "harvested_comments": [ - "Use provided model or get default", - "Just return the data divided by scaling coefficient", - "No scaling, return data as-is" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "EnhancedMeshVariable", - "is_public": true - }, - { - "name": "continuous", - "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 363, - "signature": "(self) -> bool", - "parameters": [], - "returns": "bool", - "existing_docstring": "Whether variable is continuous.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "EnhancedMeshVariable", - "is_public": true - }, - { - "name": "symbol", + "name": "unit_lat", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 368, + "file": "src/underworld3/coordinates.py", + "line": 693, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Variable symbol for LaTeX representation.", + "existing_docstring": "Latitude direction unit vector (coordinate alias for unit_SN).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "vec", + "name": "unit_depth", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 375, + "file": "src/underworld3/coordinates.py", + "line": 698, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "PETSc vector (for solver integration).", + "existing_docstring": "Depth direction unit vector (coordinate alias for unit_down).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "sym", - "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 392, - "signature": "(self)", - "parameters": [], + "name": "to_cartesian", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 704, + "signature": "(self, lon, lat, depth)", + "parameters": [ + { + "name": "lon", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "lat", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "depth", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Enhanced mathematical symbol.", + "existing_docstring": "Convert geographic coordinates to Cartesian (x, y, z).\n\nUses the mesh's ellipsoid parameters automatically.\nWhen units are active, depth should be nondimensional and returns\nnondimensional Cartesian coordinates.\n\nParameters\n----------\nlon : float or array_like\n Longitude in degrees East (-180 to 180)\nlat : float or array_like\n Geodetic latitude in degrees North (-90 to 90)\ndepth : float or array_like\n Depth below ellipsoid surface (nondimensional if units active, km otherwise)\n\nReturns\n-------\ntuple\n (x, y, z) coordinates (nondimensional if units active, km otherwise)\n\nExamples\n--------\n>>> # Import external data in geographic coordinates\n>>> tomo_lon = np.array([136.0, 136.5, 137.0])\n>>> tomo_lat = np.array([-34.0, -33.5, -33.0])\n>>> tomo_depth = np.array([10.0, 20.0, 30.0]) # km or nondimensional\n>>> x, y, z = mesh.geo.to_cartesian(tomo_lon, tomo_lat, tomo_depth)", "harvested_comments": [ - "Get base symbol from underlying variable" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "Import external data in geographic coordinates", + "km or nondimensional", + "Nondimensionalise ellipsoid for numeric coordinate conversion" ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "pack_uw_data_to_petsc", + "name": "from_cartesian", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 404, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/coordinates.py", + "line": 746, + "signature": "(self, x, y, z)", "parameters": [ { - "name": "*args", + "name": "x", "type_hint": null, "default": null, "description": "" }, { - "name": "**kwargs", + "name": "y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "z", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Pack data to PETSc format.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Convert Cartesian coordinates to geographic (lon, lat, depth).\n\nUses the mesh's ellipsoid parameters automatically.\nWhen units are active, coordinates should be nondimensional.\n\nParameters\n----------\nx : float or array_like\n X coordinate (nondimensional if units active, km otherwise)\ny : float or array_like\n Y coordinate\nz : float or array_like\n Z coordinate\n\nReturns\n-------\ntuple\n (lon, lat, depth) where:\n - lon: Longitude in degrees East (-180 to 180)\n - lat: Geodetic latitude in degrees North (-90 to 90)\n - depth: Depth (nondimensional if units active, km otherwise)\n\nExamples\n--------\n>>> # Convert mesh points to geographic for comparison with data\n>>> x, y, z = mesh.data[:, 0], mesh.data[:, 1], mesh.data[:, 2]\n>>> lon, lat, depth = mesh.geo.from_cartesian(x, y, z)", + "harvested_comments": [ + "Convert mesh points to geographic for comparison with data", + "Nondimensionalise ellipsoid for numeric coordinate conversion" ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "unpack_uw_data_from_petsc", + "name": "points_to_cartesian", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 408, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/coordinates.py", + "line": 788, + "signature": "(self, points_geo)", "parameters": [ { - "name": "*args", + "name": "points_geo", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Convert array of geographic points to Cartesian coordinates.\n\nConvenience method for importing external data.\n\nParameters\n----------\npoints_geo : array_like\n Array of shape (N, 3) with columns [lon, lat, depth]\n - lon: Longitude in degrees East\n - lat: Geodetic latitude in degrees North\n - depth: Depth in km below surface\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [x, y, z] in km\n\nExamples\n--------\n>>> # Import seismicity catalog\n>>> eq_llz = np.loadtxt(\"earthquakes.csv\", delimiter=\",\") # [lon, lat, depth]\n>>> eq_xyz = mesh.geo.points_to_cartesian(eq_llz)\n>>> # Now use with KDTree or swarm.add_particles_with_coordinates()", + "harvested_comments": [ + "Import seismicity catalog", + "[lon, lat, depth]", + "Now use with KDTree or swarm.add_particles_with_coordinates()" + ], + "status": "complete", + "needs": [], + "parent_class": "GeographicCoordinateAccessor", + "is_public": true + }, + { + "name": "points_from_cartesian", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 820, + "signature": "(self, points_xyz)", + "parameters": [ { - "name": "**kwargs", + "name": "points_xyz", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Unpack data from PETSc format.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Convert array of Cartesian points to geographic coordinates.\n\nParameters\n----------\npoints_xyz : array_like\n Array of shape (N, 3) with columns [x, y, z] in km\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [lon, lat, depth]\n\nExamples\n--------\n>>> # Export mesh coordinates to geographic\n>>> mesh_xyz = mesh.data # or mesh.CoordinateSystem.coords\n>>> mesh_llz = mesh.geo.points_from_cartesian(mesh_xyz)", + "harvested_comments": [ + "Export mesh coordinates to geographic", + "or mesh.CoordinateSystem.coords" ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "divergence", + "name": "view", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 414, + "file": "src/underworld3/coordinates.py", + "line": 861, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Vector divergence calculation.", + "existing_docstring": "Display a formatted summary of available properties and methods.\n\nThis method prints a helpful guide to the geographic coordinate system,\nshowing all available data arrays, symbolic coordinates, unit vectors,\nand conversion methods.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "GeographicCoordinateAccessor", "is_public": true }, { - "name": "gradient", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 418, + "name": "r", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1010, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Vector gradient calculation.", + "existing_docstring": "Radial distance from origin.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "curl", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 422, + "name": "theta", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1016, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Vector curl calculation.", + "existing_docstring": "Colatitude in radians (0 at north pole, \u03c0 at south pole).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "jacobian", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 426, + "name": "phi", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1022, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Vector jacobian calculation.", + "existing_docstring": "Longitude/azimuth in radians (-\u03c0 to \u03c0). Only available for 3D meshes.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "clone", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 432, + "name": "coords", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1033, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Clone the variable.", + "existing_docstring": "Spherical/polar coordinates as array.\n\nReturns mesh node coordinates in spherical/polar form:\n- 3D: (N, 3) array [r, \u03b8, \u03c6]\n- 2D: (N, 2) array [r, \u03b8]\n\nReturns\n-------\nnumpy.ndarray\n For 3D (spherical): Shape (N, 3) array where columns are:\n\n - Column 0: Radius (same units as mesh)\n - Column 1: Colatitude \u03b8 in radians (0 at north pole)\n - Column 2: Longitude \u03c6 in radians (-\u03c0 to \u03c0)\n\n For 2D (polar): Shape (N, 2) array where columns are:\n\n - Column 0: Radius (same units as mesh)\n - Column 1: Polar angle \u03b8 in radians\n\nExamples\n--------\n>>> sph_coords = mesh.X.spherical.coords\n>>> print(f\"Node 0: r={sph_coords[0,0]:.3f}, \u03b8={np.degrees(sph_coords[0,1]):.1f}\u00b0\")\n\nSee Also\n--------\nmesh.X.coords : Cartesian coordinates (x, y) or (x, y, z)\nr, theta, phi : Individual coordinate arrays", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "max", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 436, + "name": "r_sym", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1093, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Maximum value of the variable.", + "existing_docstring": "Symbolic radial coordinate.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "mean", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 440, + "name": "theta_sym", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1098, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Mean value of the variable.", + "existing_docstring": "Symbolic polar/colatitude coordinate.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "min", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 444, + "name": "phi_sym", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1103, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Minimum value of the variable.", + "existing_docstring": "Symbolic longitude coordinate (3D only).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "norm", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 448, - "signature": "(self, norm_type = None)", - "parameters": [ - { - "name": "norm_type", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "name": "unit_r", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1115, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Compute the norm of the variable.", - "harvested_comments": [ - "Mathematical usage: use SymPy Matrix norm from MathematicalMixin", - "Computational usage: delegate to PETSc norm" - ], + "existing_docstring": "Radial unit vector (outward from origin).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "load_from_h5_plex_vector", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 457, - "signature": "(self, *args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "unit_theta", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1120, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Load from HDF5 plex vector.", + "existing_docstring": "Tangential unit vector (direction of increasing \u03b8).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "write", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 461, - "signature": "(self, *args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "unit_phi", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1125, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Write variable data to HDF5 file.", + "existing_docstring": "Azimuthal unit vector (eastward, direction of increasing \u03c6). 3D only.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "sym_1d", + "name": "unit_radial", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 466, + "file": "src/underworld3/coordinates.py", + "line": 1137, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Flattened sympy view of the variable.", + "existing_docstring": "Alias for unit_r.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "save", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 470, - "signature": "(self, *args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "unit_outward", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1142, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Save variable data.", + "existing_docstring": "Outward radial unit vector (alias for unit_r).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "read_timestep", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 474, - "signature": "(self, *args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "unit_inward", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 1147, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Read timestep data.", + "existing_docstring": "Inward radial unit vector (opposite of unit_r).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "stats", + "name": "to_cartesian", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 478, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/coordinates.py", + "line": 1153, + "signature": "(self, r, theta, phi)", "parameters": [ { - "name": "*args", + "name": "r", "type_hint": null, "default": null, "description": "" }, { - "name": "**kwargs", + "name": "theta", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "phi", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Get statistics for the variable.", + "existing_docstring": "Convert spherical coordinates to Cartesian (x, y, z).\n\nParameters\n----------\nr : float or array_like\n Radial distance\ntheta : float or array_like\n Colatitude in radians (0 at north pole)\nphi : float or array_like\n Longitude in radians\n\nReturns\n-------\ntuple\n (x, y, z) Cartesian coordinates", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "sum", + "name": "from_cartesian", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 482, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/coordinates.py", + "line": 1181, + "signature": "(self, x, y, z)", "parameters": [ { - "name": "*args", + "name": "x", "type_hint": null, "default": null, "description": "" }, { - "name": "**kwargs", + "name": "y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "z", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Sum of variable values.", + "existing_docstring": "Convert Cartesian coordinates to spherical (r, \u03b8, \u03c6).\n\nParameters\n----------\nx : float or array_like\n X coordinate\ny : float or array_like\n Y coordinate\nz : float or array_like\n Z coordinate\n\nReturns\n-------\ntuple\n (r, theta, phi) where:\n\n - r: Radial distance\n - theta: Colatitude in radians (0 at north pole)\n - phi: Longitude in radians (-\u03c0 to \u03c0)", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "rbf_interpolate", + "name": "points_to_cartesian", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 486, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/coordinates.py", + "line": 1213, + "signature": "(self, points_sph)", "parameters": [ { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", + "name": "points_sph", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "RBF interpolation.", + "existing_docstring": "Convert array of spherical points to Cartesian coordinates.\n\nParameters\n----------\npoints_sph : array_like\n Array of shape (N, 3) with columns [r, theta, phi]\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [x, y, z]", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "pack_raw_data_to_petsc", + "name": "points_from_cartesian", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 490, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/coordinates.py", + "line": 1233, + "signature": "(self, points_xyz)", "parameters": [ { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", + "name": "points_xyz", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Pack raw data to PETSc format.", + "existing_docstring": "Convert array of Cartesian points to spherical coordinates.\n\nParameters\n----------\npoints_xyz : array_like\n Array of shape (N, 3) with columns [x, y, z]\n\nReturns\n-------\nndarray\n Array of shape (N, 3) with columns [r, theta, phi]", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "EnhancedMeshVariable", + "status": "complete", + "needs": [], + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "unpack_raw_data_from_petsc", + "name": "view", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 494, - "signature": "(self, *args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/coordinates.py", + "line": 1264, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Unpack raw data from PETSc format.", + "existing_docstring": "Display a formatted summary of available properties and methods.\n\nThis method prints a helpful guide to the spherical coordinate system,\nshowing all available data arrays, symbolic coordinates, unit vectors,\nand conversion methods.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "SphericalCoordinateAccessor", "is_public": true }, { - "name": "field_id", + "name": "coords", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 501, + "file": "src/underworld3/coordinates.py", + "line": 1861, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Field ID in the mesh.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Mesh vertex coordinates as a numerical array.\n\nReturns the mesh node coordinates in physical units. When the mesh has\nreference quantities set, coordinates are scaled from internal model\nunits to physical units.\n\nThis is the primary interface for accessing coordinate data for\ninitialization, visualization, and coordinate-based calculations.\n\nReturns\n-------\nnumpy.ndarray or UnitAwareArray\n Coordinate array with shape ``(N, dim)`` where ``N`` is the number\n of mesh vertices. Returns UnitAwareArray if mesh has units configured.\n\nExamples\n--------\n>>> # Get all coordinates\n>>> coords = mesh.X.coords\n>>> x_values = coords[:, 0]\n>>> y_values = coords[:, 1]\n\n>>> # Use for field initialization\n>>> temperature.array[:, 0, 0] = 300 + 100 * mesh.X.coords[:, 0]\n\n>>> # Pass to evaluation functions\n>>> values = uw.function.evaluate(expression, mesh.X.coords)\n\n>>> # Get bounds\n>>> x_min, x_max = mesh.X.coords[:, 0].min(), mesh.X.coords[:, 0].max()\n\nSee Also\n--------\nunits : Coordinate units (e.g., 'kilometer').\nmesh.points : Alias for mesh.X.coords.\nvar.coords : Variable-specific DOF coordinates (may differ from mesh vertices).", + "harvested_comments": [ + "Get all coordinates", + "Use for field initialization", + "Pass to evaluation functions", + "Apply scaling to convert model coordinates to physical coordinates", + "Wrap with unit-aware array if units are specified" + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "fn", + "name": "units", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 506, + "file": "src/underworld3/coordinates.py", + "line": 1921, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Function representation.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Physical units of the coordinate system.\n\nReturns the units for coordinates accessed via ``.coords``. This is\nderived from the mesh's reference quantities and indicates the physical\nscale of the coordinate data.\n\nReturns\n-------\nstr or None\n Unit string (e.g., 'kilometer', 'meter'), or None if no reference\n quantities are set.\n\nExamples\n--------\n>>> print(mesh.X.units) # 'kilometer'\n>>> coords = mesh.X.coords # Coordinates in kilometers\n\nSee Also\n--------\ncoords : Coordinate data array.\nmesh.units : Same as mesh.X.units.", + "harvested_comments": [ + "'kilometer'", + "Coordinates in kilometers" + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "ijk", + "name": "with_units", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 511, + "file": "src/underworld3/coordinates.py", + "line": 1948, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "IJK coordinates.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Coordinate symbols with unit information.\n\nDEPRECATED (2025-11-26): Following the Transparent Container Principle,\nthis now returns raw coordinate symbols. Units are derived on demand via\nuw.get_units() which finds the _units attribute on coordinate atoms.\n\nExamples:\n >>> x, y = mesh.X.with_units # Same as mesh.X[0], mesh.X[1]\n >>> area = x * y # Raw SymPy Mul; uw.get_units(area) \u2192 km**2\n >>> uw.get_units(x) # \u2192 kilometer (derived from _units attribute)\n\nReturns:\n tuple: Coordinate symbols (x, y) or (x, y, z)", + "harvested_comments": [ + "Same as mesh.X[0], mesh.X[1]", + "Raw SymPy Mul; uw.get_units(area) \u2192 km**2", + "\u2192 kilometer (derived from _units attribute)", + "TRANSPARENT CONTAINER PRINCIPLE (2025-11-26):", + "Just return raw coordinates. They have _units attributes from patching," + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "instance_number", + "name": "shape", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 516, + "file": "src/underworld3/coordinates.py", + "line": 1970, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Instance number.", + "existing_docstring": "Shape of the symbolic coordinate matrix.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "old_data", + "name": "is_symbol", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 521, + "file": "src/underworld3/coordinates.py", + "line": 1996, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Legacy data property (for testing).", + "existing_docstring": "SymPy type check - CoordinateSystem contains symbols but is not itself a symbol.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "uw_object_counter", + "name": "is_Matrix", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 526, + "file": "src/underworld3/coordinates.py", + "line": 2001, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Object counter from API tools.", + "existing_docstring": "SymPy type check - CoordinateSystem behaves like a Matrix.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "transfer_data_from", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 532, - "signature": "(self, source_var)", - "parameters": [ - { - "name": "source_var", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "is_scalar", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2006, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Transfer data from another variable using global_evaluate.\n\nThis enables persistent variable identity across mesh changes.\n\nNote on Symbol Disambiguation (2025-12-15):\n This method transfers DATA, not symbolic expressions.\n The source and target variables have distinct symbolic identities\n (source_var.sym != self.sym) even if they share the same name.\n\n For transferring expressions containing coordinates, use explicit\n substitution: expr.subs({old_mesh.N.x: new_mesh.N.x, ...})\n\n See: docs/developer/design/SYMBOL_DISAMBIGUATION_2025-12.md\n\nArgs:\n source_var: Source variable to transfer data from\n\nReturns:\n bool: True if transfer successful", + "existing_docstring": "SymPy type check - CoordinateSystem is a Matrix, not a scalar.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "EnhancedMeshVariable", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "qualified_name", + "name": "is_number", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 563, - "signature": "(self) -> Optional[str]", + "file": "src/underworld3/coordinates.py", + "line": 2011, + "signature": "(self)", "parameters": [], - "returns": "Optional[str]", - "existing_docstring": "Qualified name for collision resolution.\n\nReturns the fully qualified name used in the model registry\nto resolve namespace collisions (e.g., 'velocity_mesh123456').", + "returns": null, + "existing_docstring": "SymPy type check - CoordinateSystem is not a number.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "is_persistent", + "name": "is_commutative", "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 573, - "signature": "(self) -> bool", + "file": "src/underworld3/coordinates.py", + "line": 2016, + "signature": "(self)", "parameters": [], - "returns": "bool", - "existing_docstring": "Whether this variable has persistence capabilities.", + "returns": null, + "existing_docstring": "SymPy type check - delegate to underlying matrix.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "view", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 597, - "signature": "(self)", + "name": "X", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2096, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Display detailed information about the enhanced variable including units.\n\nShows variable name, dimensions, shape, units information, and sample data.", + "returns": "sympy.Matrix", + "existing_docstring": "Cartesian coordinate system providing symbolic and numerical access.\n\nThis is the primary interface for mesh coordinates, providing both:\n\n- **Symbolic access**: ``mesh.X[0]``, ``mesh.X[1]`` for use in equations\n- **Numerical access**: ``mesh.X.coords`` for coordinate data arrays\n\nReturns\n-------\nsympy.Matrix\n Symbolic coordinate matrix that also supports ``.coords`` for data.\n\nExamples\n--------\n>>> # Symbolic access for equations\n>>> x, y = mesh.X\n>>> temperature_expr = 300 + 100 * x\n\n>>> # Component access\n>>> x_coord = mesh.X[0] # Symbolic x-coordinate\n>>> y_coord = mesh.X[1] # Symbolic y-coordinate\n\n>>> # Numerical coordinate data\n>>> coords = mesh.X.coords # (N, dim) array of vertex positions\n>>> x_values = mesh.X.coords[:, 0]\n\n>>> # Coordinate units (if set)\n>>> units = mesh.X.units # e.g., 'kilometer'\n\nSee Also\n--------\nR : Natural (curvilinear) coordinates for spherical/cylindrical meshes.\ngeo : Geographic coordinates for regional spherical meshes.\nN : Raw BaseScalar coordinates for JIT compilation.", "harvested_comments": [ - "Units information", - "Persistence information", - "Sample data (first few elements)", - "Mathematical capabilities", - "Allow chaining" + "Symbolic access for equations", + "Component access", + "Symbolic x-coordinate", + "Symbolic y-coordinate", + "Numerical coordinate data" ], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "create_enhanced_mesh_variable", - "kind": "function", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 638, - "signature": "(varname: Union[str, list], mesh: 'Mesh', num_components: Union[int, tuple] = None, persistent: bool = True, units: Optional[str] = None, **kwargs) -> EnhancedMeshVariable", - "parameters": [ - { - "name": "varname", - "type_hint": "Union[str, list]", - "default": null, - "description": "" - }, - { - "name": "mesh", - "type_hint": "'Mesh'", - "default": null, - "description": "" - }, - { - "name": "num_components", - "type_hint": "Union[int, tuple]", - "default": "None", - "description": "" - }, - { - "name": "persistent", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "units", - "type_hint": "Optional[str]", - "default": "None", - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": "EnhancedMeshVariable", - "existing_docstring": "Factory function to create an enhanced mesh variable.\n\nThis is a convenience function that creates an EnhancedMeshVariable\nwith persistence enabled by default.\n\nArgs:\n varname: Variable name\n mesh: Mesh object\n num_components: Number of components\n persistent: Enable persistence (default True)\n units: Units specification\n **kwargs: Additional arguments\n\nReturns:\n EnhancedMeshVariable instance", + "name": "x", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2136, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Alias for natural coordinates (lowercase convention).", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "demonstrate_enhanced_variables", - "kind": "function", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 702, - "signature": "()", + "name": "N", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2141, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Demonstration of enhanced variables with units and mathematical operations.\n\nThis function shows how the enhanced variables integrate mathematical\noperations with units checking and dimensional analysis.", - "harvested_comments": [ - "Create a simple mesh", - "Create enhanced mesh variables with units", - "Test mathematical operations", - "Component access", - "Vector operations" - ], - "status": "partial", + "returns": "sympy.Matrix", + "existing_docstring": "Raw BaseScalar coordinates for derivatives and JIT compilation.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "with_units", - "kind": "function", - "file": "src/underworld3/function/__init__.py", - "line": 94, - "signature": "(sympy_expr, name = None, units = None)", - "parameters": [ - { - "name": "sympy_expr", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "name", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "units", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Wrap a SymPy expression in a unit-aware object with .units and .to() methods.\n\nThis is particularly useful for derivatives and other SymPy operations that\nreturn plain SymPy expressions instead of unit-aware objects.\n\nParameters\n----------\nsympy_expr : sympy expression\n The SymPy expression to wrap (e.g., from temperature.diff(y))\nname : str, optional\n Optional name for the expression (auto-generated if not provided)\nunits : str, optional\n Explicit units for the expression. If provided, these units are used\n instead of trying to extract units from the expression. This is useful\n for derivatives where units are known from the derivative operation.\n\nReturns\n-------\nUWexpression\n Unit-aware expression with .units and .to() methods\n\nExamples\n--------\n>>> # Derivative returns plain SymPy\n>>> dTdy_sympy = temperature.diff(y)[0]\n>>>\n>>> # Wrap it to get unit-aware object\n>>> dTdy = uw.with_units(dTdy_sympy)\n>>> dTdy.units # kelvin / kilometer\n>>> dTdy.to(\"K/mm\") # Unit conversion works!\n\n>>> # Also works with other SymPy operations\n>>> combined = uw.with_units(2 * temperature.sym + pressure.sym)\n\n>>> # Explicit units for derivatives\n>>> dTdy_elem = uw.with_units(derivative_expr, units=\"kelvin / kilometer\")", - "harvested_comments": [ - "Derivative returns plain SymPy", - "Wrap it to get unit-aware object", - "kelvin / kilometer", - "Unit conversion works!", - "Also works with other SymPy operations" + "name": "R", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2146, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Natural (curvilinear) coordinates :math:`(r, \\theta, \\phi)` or geographic.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "derivative", - "kind": "function", - "file": "src/underworld3/function/__init__.py", - "line": 162, - "signature": "(expression, variable, evaluate = True)", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "variable", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "evaluate", - "type_hint": null, - "default": "True", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Obtain symbolic derivatives of any underworld function, correctly handling sub-expressions / constants.\n\nUWCoordinates from mesh.X work transparently with this function and with\nsympy.diff() directly - both approaches now produce identical results:\n\n x, y = mesh.X\n result = uw.function.derivative(expr, y) # This function\n result = sympy.diff(expr, y) # Also works (since Dec 2025)\n\nArgs:\n expression: The expression to differentiate\n variable: The variable to differentiate with respect to (UWCoordinate or BaseScalar)\n evaluate (bool): If True, evaluate immediately. If False, return deferred derivative.\n\nReturns:\n The derivative (evaluated SymPy expression or UWDerivativeExpression)", - "harvested_comments": [ - "This function", - "Also works (since Dec 2025)", - "Note: Since December 2025, UWCoordinate subclasses BaseScalar with __eq__/__hash__", - "that match the original N.x/N.y/N.z, so no conversion is needed anymore.", - "sympy.diff() works directly with UWCoordinates." + "name": "r", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2151, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Symbolic natural coordinate variables (for mathematical expressions).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "fdiff", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 56, - "signature": "def fdiff(self, argindex):", + "name": "xR", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2156, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "\n We provide an explicit derivative function.\n This allows us to control the way derivative objects are printed,\n but in the user interface, but more critically it allows us to\n patch in code printing implementation for derivatives objects,\n as utilised in `_jitextension.py`.\n", + "returns": "sympy.Matrix", + "existing_docstring": "Alias for ``R`` (backward compatibility).", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "get_structure", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 53, - "signature": "(self, coords: np.ndarray, dofcount: int)", - "parameters": [ - { - "name": "coords", - "type_hint": "np.ndarray", - "default": null, - "description": "" - }, - { - "name": "dofcount", - "type_hint": "int", - "default": null, - "description": "" - } - ], + "name": "geo", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2161, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Get cached CachedDMInterpolationInfo or None.\n\nParameters\n----------\ncoords : ndarray\n Evaluation coordinates\ndofcount : int\n Total DOF count for current variables\n\nReturns\n-------\ncached_info : CachedDMInterpolationInfo or None\n Cached structure object, or None if not cached or disabled", + "existing_docstring": "Geographic coordinates for GEOGRAPHIC meshes.\n\nProvides access to longitude, latitude, depth coordinates\nand geographic basis vectors on ellipsoidal (WGS84) meshes.\n\nReturns\n-------\nGeographicCoordinateAccessor\n Object with .lon, .lat, .depth, .coords, unit vectors, etc.\n Use .view() for a complete summary of available properties.\n\nRaises\n------\nAttributeError\n If coordinate system is not GEOGRAPHIC. The error message\n indicates what coordinate system IS available for this mesh.\n\nExamples\n--------\n>>> lon = mesh.X.geo.lon # Longitude data array\n>>> geo_coords = mesh.X.geo.coords # (N, 3) array [lon, lat, depth]\n>>> mesh.X.geo.view() # Show all available properties", "harvested_comments": [ - "Caching disabled", - "Compute cache key", - "Check cache" + "Longitude data array", + "(N, 3) array [lon, lat, depth]", + "Show all available properties", + "Provide helpful error message indicating what IS available" ], - "status": "complete", - "needs": [], - "parent_class": "DMInterpolationCache", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "store_structure", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 88, - "signature": "(self, coords: np.ndarray, dofcount: int, cached_info)", - "parameters": [ - { - "name": "coords", - "type_hint": "np.ndarray", - "default": null, - "description": "" - }, - { - "name": "dofcount", - "type_hint": "int", - "default": null, - "description": "" - }, - { - "name": "cached_info", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "spherical", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2212, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Store CachedDMInterpolationInfo in cache.\n\nParameters\n----------\ncoords : ndarray\n Evaluation coordinates\ndofcount : int\n Total DOF count\ncached_info : CachedDMInterpolationInfo\n Cython wrapper object containing DMInterpolation structure", + "existing_docstring": "Spherical/polar coordinates for SPHERICAL and CYLINDRICAL2D meshes.\n\nProvides access to radius and angle coordinates, plus basis vectors:\n- 3D (SPHERICAL): r, \u03b8 (colatitude), \u03c6 (longitude)\n- 2D (CYLINDRICAL2D/Annulus): r, \u03b8 (polar angle)\n\nReturns\n-------\nSphericalCoordinateAccessor\n Object with .r, .theta, .coords, unit vectors, etc.\n For 3D also .phi. Use .view() for a complete summary.\n\nRaises\n------\nAttributeError\n If coordinate system is not SPHERICAL or CYLINDRICAL2D.\n The error message indicates what IS available for this mesh.\n\nExamples\n--------\n>>> r = mesh.X.spherical.r # Radius data array\n>>> sph_coords = mesh.X.spherical.coords # (N, 2) or (N, 3) array\n>>> mesh.X.spherical.view() # Show all available properties", "harvested_comments": [ - "Don't cache if disabled", - "Python GC keeps it alive" + "Radius data array", + "(N, 2) or (N, 3) array", + "Show all available properties", + "Provide helpful error message indicating what IS available" ], "status": "partial", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "invalidate_all", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 120, - "signature": "(self, reason: str = 'manual')", - "parameters": [ - { - "name": "reason", - "type_hint": "str", - "default": "'manual'", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Clear entire cache and destroy all DMInterpolation structures.\n\nIMPORTANT: Must be called from Cython to properly destroy C structures!\nThis method only clears the Python-side tracking.", + "name": "rRotN", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2264, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Rotation matrix from Cartesian (N) to natural (R) coordinates.\n\nTransforms vectors from Cartesian basis to the local curvilinear basis.\nFor spherical: rows are :math:`(\\hat{r}, \\hat{\\theta}, \\hat{\\phi})`.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "invalidate_coords", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 133, - "signature": "(self, coords: np.ndarray)", - "parameters": [ - { - "name": "coords", - "type_hint": "np.ndarray", - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Invalidate all cache entries for specific coordinates.", - "harvested_comments": [ - "Remove all entries with this coord hash (different dofcounts)" - ], + "name": "xRotN", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2273, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Rotation matrix from Cartesian (N) to Cartesian (X) - typically identity.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "get_stats", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 144, - "signature": "(self) -> dict", + "name": "geoRotN", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2278, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": "dict", - "existing_docstring": "Get cache performance statistics.\n\nReturns\n-------\ndict with metrics:\n - requests: Total get() calls\n - hits: Cache hits\n - misses: Cache misses\n - hit_rate: Percentage of hits\n - entries: Current cache size\n - invalidations: Total invalidation events", + "returns": "sympy.Matrix", + "existing_docstring": "Ellipsoidal rotation matrix for GEOGRAPHIC coordinate systems.\n\nTransforms Cartesian vectors to the local geographic frame:\n- Row 0: geodetic up (perpendicular to ellipsoid surface)\n- Row 1: north (meridional, along ellipsoid surface)\n- Row 2: east (azimuthal, along ellipsoid surface)\n\nThis is the ellipsoidal equivalent of ``rRotN`` for spherical coordinates.\nFor a sphere (a=b), this reduces to the spherical rotation matrix.\n\nFor an ellipsoid, the geodetic normal differs from the radial direction\nby up to ~10 arcminutes at mid-latitudes, which is significant for\nregional models at scales of 10-100 km.\n\nReturns\n-------\nsympy.Matrix\n 3\u00d73 rotation matrix, or None if not GEOGRAPHIC coordinate system.\n\nExamples\n--------\nTransform a Cartesian velocity to geographic components:\n\n>>> v_cartesian = sympy.Matrix([[vx, vy, vz]])\n>>> v_geo = mesh.CoordinateSystem.geoRotN * v_cartesian.T\n>>> v_up, v_north, v_east = v_geo[0], v_geo[1], v_geo[2]", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "print_stats", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 169, - "signature": "(self)", + "name": "unit_e_0", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2312, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Print cache statistics (user-facing).", + "returns": "sympy.Matrix", + "existing_docstring": "First natural basis vector (radial :math:`\\hat{r}` for curvilinear).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "reset_stats", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 183, - "signature": "(self)", + "name": "unit_e_1", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2317, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Reset statistics (but keep cached entries).", + "returns": "sympy.Matrix", + "existing_docstring": "Second natural basis vector (:math:`\\hat{\\theta}` for curvilinear).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", - "is_public": true - }, - { - "name": "simplify_units", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 31, - "signature": "(units)", - "parameters": [ - { - "name": "units", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Simplify combined units to human-readable form.\n\nConverts complex unit expressions (e.g., megayear * centimeter / year)\nto compact, human-friendly forms (e.g., kilometer) using Pint's\n:meth:`to_compact` method.\n\nParameters\n----------\nunits : pint.Unit or None\n The units to simplify.\n\nReturns\n-------\npint.Unit or None\n Simplified units with appropriate prefixes, or None if input is None.", - "harvested_comments": [ - "Create a quantity with value 1 and these units, then simplify", - "If simplification fails, return original units" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "unwrap_expression", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 176, - "signature": "(expr, mode = 'nondimensional', depth = None)", - "parameters": [ - { - "name": "expr", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "mode", - "type_hint": null, - "default": "'nondimensional'", - "description": "" - }, - { - "name": "depth", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Unified unwrapping of UW expressions.\n\nThis is the single entry point for all expression unwrapping needs.\n\nArgs:\n expr: Expression to unwrap (UWexpression, SymPy expr, or value)\n mode: 'nondimensional' - for JIT compilation and evaluation (uses .data)\n 'dimensional' - for user display (uses .value)\n 'symbolic' - just expand .sym structure\n depth: Maximum expansion depth (None = complete expansion)\n\nReturns:\n Pure SymPy expression with all UW atoms expanded", - "harvested_comments": [ - "Extract sym if needed", - "Fixed-point iteration (or depth-limited)", - "Safety limit" - ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "is_constant_expr", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 227, - "signature": "(fn)", - "parameters": [ - { - "name": "fn", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Check if expression has no mesh variable dependencies.\n\nAn expression is \"constant\" in the Underworld sense if it doesn't\ndepend on mesh coordinates, mesh variables, or applied functions.\n\nParameters\n----------\nfn : sympy.Expr or UWexpression\n Expression to check.\n\nReturns\n-------\nbool\n True if the expression has no spatial dependencies.", + "name": "unit_e_2", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2322, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Third natural basis vector (:math:`\\hat{\\phi}` for spherical, None for 2D).", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "extract_expressions", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 247, - "signature": "(fn)", - "parameters": [ - { - "name": "fn", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Extract all UWexpression atoms from a SymPy expression.\n\nParameters\n----------\nfn : sympy.Expr or UWexpression\n Expression to search.\n\nReturns\n-------\nset\n Set of :class:`UWexpression` objects found in the expression tree.", - "harvested_comments": [ - "exhaustion criterion" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "extract_expressions_and_functions", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 282, - "signature": "(fn)", - "parameters": [ - { - "name": "fn", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Extract all UWexpression, Function, and coordinate atoms.\n\nRecursively searches an expression tree for Underworld-specific\natoms including expressions, applied functions, and coordinate\nbase scalars.\n\nParameters\n----------\nfn : sympy.Expr or UWexpression\n Expression to search.\n\nReturns\n-------\nset\n Set of UWexpression, Function, and BaseScalar objects.", - "harvested_comments": [ - "Handle UWQuantity objects - they don't have atoms() method", - "exhaustion criterion" + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "expand", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 358, - "signature": "(expr, depth = None, simplify_result = False)", - "parameters": [ - { - "name": "expr", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "depth", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "simplify_result", - "type_hint": null, - "default": "False", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Expand UW expression to reveal SymPy structure for inspection.\n\nThis function recursively expands nested UW expressions to reveal their\nunderlying SymPy representation. It's designed for user inspection and\ndebugging - use dimensional values (not scaled).\n\nArgs:\n expr: UW expression to expand\n depth (int, optional): Maximum expansion depth. None = full expansion\n simplify_result (bool): If True, apply SymPy simplification\n\nReturns:\n Pure SymPy expression with all UW wrappers removed (dimensional values)", + "name": "unit_i", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2330, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Cartesian unit vector :math:`\\hat{i}` (x-direction).", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "unwrap", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 382, - "signature": "(fn, depth = None, keep_constants = True, return_self = True)", - "parameters": [ - { - "name": "fn", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "depth", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "keep_constants", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "return_self", - "type_hint": null, - "default": "True", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Expand UW expression to reveal SymPy structure.\n\nArgs:\n fn: Expression to unwrap\n depth: Maximum expansion depth (None = complete)\n keep_constants: If False, use nondimensional mode (for JIT)\n return_self: If False, use nondimensional mode (for JIT)\n\nReturns:\n Unwrapped SymPy expression", - "harvested_comments": [ - "JIT compilation path - use nondimensional mode", - "Display path - use dimensional mode" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "unwrap_for_evaluate", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 403, - "signature": "(expr, scaling_active = None)", - "parameters": [ - { - "name": "expr", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "scaling_active", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Unwrap expression for evaluate/lambdify path with proper unit handling.\n\nType-based dispatch (2025-12 UWCoordinate design):\n- UWCoordinate: unwrap to BaseScalar (placeholder, NO scaling)\n- UWexpression with units: nondimensionalize via .data\n- UWexpression without units: recursively unwrap .sym\n- UWQuantity: nondimensionalize via .data\n- BaseScalar/MeshVariable.sym: pass through unchanged\n\nReturns:\n tuple: (unwrapped_expr, result_dimensionality)", - "harvested_comments": [ - "Step 1: Get expression dimensionality", - "IMPORTANT: For UWexpression, try the wrapper FIRST because it stores units", - "via ._value_with_units. The raw .sym expression has no unit metadata.", - "Try wrapper first (has unit info from arithmetic)", - "If wrapper has no units, try the .sym expression (may contain unit-aware variables)" - ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "substitute_expr", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 518, - "signature": "(fn, sub_expr, keep_constants = True, return_self = True)", - "parameters": [ - { - "name": "fn", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "sub_expr", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "keep_constants", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "return_self", - "type_hint": null, - "default": "True", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Substitute a specific expression throughout.", + "name": "unit_j", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2335, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Cartesian unit vector :math:`\\hat{j}` (y-direction).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true - }, - { - "name": "rename", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 664, - "signature": "(self, new_display_name: str) -> 'UWexpression'", - "parameters": [ - { - "name": "new_display_name", - "type_hint": "str", - "default": null, - "description": "" - } - ], - "returns": "'UWexpression'", - "existing_docstring": "Change the display name of this expression without changing its identity.\n\nThis allows customizing how the expression appears in LaTeX output\nand string representations while preserving its symbolic identity\n(hash and equality remain based on the original name and _uw_id).\n\nParameters\n----------\nnew_display_name : str\n The new name to use for display purposes (typically LaTeX).\n\nReturns\n-------\nUWexpression\n Returns self to allow method chaining.\n\nExamples\n--------\n>>> viscosity = uw.expression(\"eta\", 1e21)\n>>> viscosity.rename(r\"\\eta_{\\mathrm{mantle}}\")\n>>> print(viscosity) # Shows renamed version\n\nNotes\n-----\nThe original symbol name (self.name) is preserved for:\n- SymPy identity (hash, equality)\n- Pickling/serialization\n- Expression matching in solvers\n\nOnly the display representation changes via _latex() and _sympystr().", - "harvested_comments": [ - "Shows renamed version" - ], - "status": "complete", - "needs": [], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "sym", + "name": "unit_k", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 802, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2340, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Get the symbolic/numeric value.", + "returns": "sympy.Matrix", + "existing_docstring": "Cartesian unit vector :math:`\\hat{k}` (z-direction, None for 2D).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "sym", + "name": "unit_ijk", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 807, - "signature": "(self, new_value)", + "file": "src/underworld3/coordinates.py", + "line": 2347, + "signature": "(self, dirn) -> sympy.Matrix", "parameters": [ { - "name": "new_value", + "name": "dirn", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Update the wrapped value.", - "harvested_comments": [ - "TRANSPARENT CONTAINER PRINCIPLE: Store the object directly", - "Keep the full UWQuantity!" - ], + "returns": "sympy.Matrix", + "existing_docstring": "Return Cartesian unit vector for direction index (0=i, 1=j, 2=k).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "copy", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 819, - "signature": "(self, other)", - "parameters": [ - { - "name": "other", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Copy the symbolic value and metadata from another expression.\n\nThis method updates this expression's content while preserving its identity\n(same Python id). Used by constitutive model parameter setters to enable\nlazy evaluation - the expression container stays the same, but its content\ncan be updated.\n\nParameters\n----------\nother : UWexpression or UWQuantity or sympy.Basic or number\n The source to copy from. If UWexpression, copies both ._sym and\n any unit metadata. Otherwise, updates .sym directly.\n\nNotes\n-----\nThis follows the same pattern as ExpressionDescriptor.__set__ in _api_tools.py\nto ensure consistent behavior for expression updates.", + "name": "unit_vertical", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2356, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Primary vertical direction for this coordinate system", "harvested_comments": [ - "Copy the symbolic value", - "Copy unit metadata for compatibility with older patterns", - "(though Transparent Container principle derives these from _sym)", - "For UWQuantity, numbers, sympy expressions - use .sym setter" + "In Cartesian, vertical is the last coordinate direction", + "y-direction in 2D", + "z-direction in 3D", + "In cylindrical 2D, \"vertical\" is ambiguous but typically means Cartesian y", + "In spherical, \"vertical\" typically means radial outward" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "value", + "name": "unit_horizontal", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 855, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2376, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Get the dimensional value of the wrapped thing.", + "returns": "sympy.Matrix", + "existing_docstring": "Primary horizontal direction for this coordinate system", "harvested_comments": [ - "TRANSPARENT CONTAINER: Always derive from _sym (the wrapped object)" + "x-direction", + "In cylindrical, horizontal could be radial or tangential - choose radial as primary", + "radial direction", + "In spherical, horizontal is typically tangential (theta direction)", + "meridional direction" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "data", + "name": "unit_horizontal_0", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 863, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2392, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Get the non-dimensional value for computation.", + "returns": "sympy.Matrix", + "existing_docstring": "First horizontal direction (alias for unit_horizontal)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "units", + "name": "unit_horizontal_1", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 881, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2397, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Get units from the wrapped thing (if it has units).", + "returns": "sympy.Matrix", + "existing_docstring": "Second horizontal direction (for 3D systems)", "harvested_comments": [ - "TRANSPARENT CONTAINER: Always derive from _sym" + "y-direction in 3D Cartesian", + "tangential direction", + "azimuthal direction" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "has_units", + "name": "unit_radial", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 889, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2414, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Check if the wrapped thing has units.", + "returns": "sympy.Matrix", + "existing_docstring": "Radial direction (for cylindrical/spherical coordinate systems)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "dimensionality", + "name": "unit_tangential", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 894, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2429, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Get dimensionality from the wrapped thing.", - "harvested_comments": [ - "TRANSPARENT CONTAINER: Always derive from _sym" + "returns": "sympy.Matrix", + "existing_docstring": "Tangential direction (for cylindrical coordinate systems)", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "CoordinateSystem", + "is_public": true + }, + { + "name": "unit_meridional", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 2442, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Meridional direction (for spherical coordinate systems)", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "expression", + "name": "unit_azimuthal", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 902, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2452, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": null, - "existing_docstring": "Get the unwrapped expression.", + "returns": "sympy.Matrix", + "existing_docstring": "Azimuthal direction (for spherical coordinate systems)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "expression_number", + "name": "geometric_dimension_names", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 915, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2462, + "signature": "(self) -> list", "parameters": [], - "returns": null, - "existing_docstring": "Unique number of the expression instance.", + "returns": "list", + "existing_docstring": "Names of geometric dimensions for this coordinate system", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "quantity", + "name": "primary_directions", "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 924, - "signature": "(self)", + "file": "src/underworld3/coordinates.py", + "line": 2479, + "signature": "(self) -> dict", "parameters": [], - "returns": null, - "existing_docstring": "Get the wrapped quantity for numeric arithmetic with units.\n\nReturns the underlying UWQuantity if one was provided, or creates\none from the value.", + "returns": "dict", + "existing_docstring": "Dictionary of all available geometric directions for this mesh type", "harvested_comments": [ - "TRANSPARENT CONTAINER: Derive from _sym (the wrapped object)" + "Add coordinate-system-specific directions" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "to", + "name": "create_line_sample", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 941, - "signature": "(self, target_units: str) -> 'UWexpression'", + "file": "src/underworld3/coordinates.py", + "line": 2527, + "signature": "(self, start_point, direction_vector, length, num_points = 50)", "parameters": [ { - "name": "target_units", - "type_hint": "str", + "name": "start_point", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "direction_vector", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "length", + "type_hint": null, "default": null, "description": "" + }, + { + "name": "num_points", + "type_hint": null, + "default": "50", + "description": "" } ], - "returns": "'UWexpression'", - "existing_docstring": "Convert to different units.\n\nDelegates to uw.convert_units() for the actual conversion.\n\nParameters\n----------\ntarget_units : str\n Target units (e.g., \"m/s\", \"km\", \"degC\")\n\nReturns\n-------\nUWexpression\n New expression with converted value and units\n\nExamples\n--------\n>>> radius = uw.expression(\"r\", uw.quantity(6370, \"km\"))\n>>> radius_m = radius.to(\"m\")\n>>> print(radius_m.value) # 6370000.0", + "returns": null, + "existing_docstring": "Create sample points along a line defined by sympy expressions.\n\nParameters\n----------\nstart_point : list or numpy.ndarray\n Starting point coordinates in Cartesian space\ndirection_vector : sympy.Matrix\n Direction vector (should be unit vector for accurate length)\nlength : float\n Length of the line to sample\nnum_points : int, optional\n Number of sample points to generate\n\nReturns\n-------\ndict\n Dictionary containing:\n - 'cartesian_coords': numpy array of Cartesian coordinates for global_evaluate()\n - 'natural_coords': numpy array of natural coordinates for plotting\n - 'parameters': numpy array of parameter values along the line (0 to length)", + "harvested_comments": [ + "Create parameter values along the line", + "Convert start point to numpy array", + "Generate Cartesian coordinates by evaluating the direction vector", + "Get coordinate symbols", + "Current point = start + t * direction" + ], + "status": "complete", + "needs": [], + "parent_class": "CoordinateSystem", + "is_public": true + }, + { + "name": "create_profile_sample", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 2652, + "signature": "(self, profile_type, **params)", + "parameters": [ + { + "name": "profile_type", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**params", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create sample points for common profile types in this coordinate system.\n\nParameters\n----------\nprofile_type : str\n Type of profile to create. Options depend on coordinate system:\n - Cartesian: 'horizontal', 'vertical', 'diagonal'\n - Cylindrical: 'radial', 'tangential', 'vertical'\n - Spherical: 'radial', 'meridional', 'azimuthal'\n**params\n Profile-specific parameters (see individual profile documentation)\n\nReturns\n-------\ndict\n Dictionary containing:\n - 'cartesian_coords': numpy array of Cartesian coordinates for global_evaluate()\n - 'natural_coords': numpy array of natural coordinates for plotting\n - 'parameters': numpy array of parameter values along the profile", "harvested_comments": [], "status": "complete", "needs": [], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "to_base_units", + "name": "zero_matrix", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 966, - "signature": "(self) -> 'UWexpression'", - "parameters": [], - "returns": "'UWexpression'", - "existing_docstring": "Convert to SI base units.\n\nDelegates to uw.to_base_units() for the actual conversion.\n\nReturns\n-------\nUWexpression\n New expression with value in SI base units\n\nExamples\n--------\n>>> velocity = uw.expression(\"v\", uw.quantity(100, \"km/h\"))\n>>> velocity_si = velocity.to_base_units()\n>>> print(velocity_si.value) # 27.78 (m/s)", + "file": "src/underworld3/coordinates.py", + "line": 2911, + "signature": "(self, shape)", + "parameters": [ + { + "name": "shape", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Matrix of spatial coordinates equivalent to zeros (but still dependent on X) -\nAdd this when you have a matrix with a mix of constants and functions - sympy / numpy\ncan become upset if the constants are not specific functions too.", "harvested_comments": [ - "27.78 (m/s)" + "Direct construction to avoid SymPy Matrix scalar multiplication issues" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": "CoordinateSystem", "is_public": true }, { - "name": "to_reduced_units", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 986, - "signature": "(self) -> 'UWexpression'", + "name": "petsc_fvm_get_min_radius", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 15, + "signature": "def petsc_fvm_get_min_radius(mesh) -> float:", "parameters": [], - "returns": "'UWexpression'", - "existing_docstring": "Simplify units by canceling common factors.\n\nDelegates to uw.to_reduced_units() for the actual simplification.\n\nReturns\n-------\nUWexpression\n New expression with simplified units", + "returns": null, + "existing_docstring": "\n This method returns the minimum distance from any cell centroid to a face.\n It wraps to the PETSc `DMPlexGetMinRadius` routine.\n", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "to_compact", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1000, - "signature": "(self) -> 'UWexpression'", + "name": "petsc_fvm_get_local_cell_sizes", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 36, + "signature": "def petsc_fvm_get_local_cell_sizes(mesh) -> np.array:", "parameters": [], - "returns": "'UWexpression'", - "existing_docstring": "Convert to most human-readable unit representation.\n\nAutomatically chooses unit prefixes (kilo, mega, micro, etc.)\nto make the number more readable.\n\nDelegates to uw.to_compact() for the actual conversion.\n\nReturns\n-------\nUWexpression\n New expression with compact units\n\nExamples\n--------\n>>> length = uw.expression(\"L\", uw.quantity(0.001, \"km\"))\n>>> length_compact = length.to_compact()\n>>> print(length_compact) # 1.0 [meter]", - "harvested_comments": [ - "1.0 [meter]" + "returns": null, + "existing_docstring": "\n This method returns the minimum distance from any cell centroid to a face.\n It wraps to the PETSc `DMPlexGetMinRadius` routine.\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": null, + "is_public": true + }, + { + "name": "petsc_dmplex_load_local_vector", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 63, + "signature": "def petsc_dmplex_load_local_vector(dm, viewer, sectiondm, sf, data_name):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Load a DMPlex checkpoint section and vector through PETSc's local-vector path.\n\n petsc4py's DMPlex.sectionLoad wrapper always requests both the global and\n local data SFs. For restart files whose section contains local overlap dofs,\n the global-data SF construction can fail before the local path is usable.\n This wrapper follows the PETSc API directly and requests only localDataSF.\n", + "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "atoms", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1035, - "signature": "(self, *types)", - "parameters": [ - { - "name": "*types", - "type_hint": null, - "default": null, - "description": "" - } + "name": "petsc_dm_create_submesh_from_label", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 91, + "signature": "def petsc_dm_create_submesh_from_label(incoming_dm, label_name, label_value, marked_faces=False):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Extract a submesh from a DMPlex using a label.\n\n Wraps DMPlexCreateSubmesh: returns a new DMPlex containing only\n cells (and their closures) that have the given value in the\n specified label.\n\n Parameters\n ----------\n incoming_dm : PETSc.DM\n The source DMPlex.\n label_name : str\n Name of the DM label to filter on.\n label_value : int\n Stratum value to select.\n marked_faces : bool\n If True, the label marks faces; if False, marks cells.\n\n Returns\n -------\n PETSc.DM\n The submesh DMPlex.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "petsc_dm_filter_by_label", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 131, + "signature": "def petsc_dm_filter_by_label(incoming_dm, label_name, label_value):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Extract a full-dimension submesh containing only cells with the\n given label value. Uses DMPlexFilter.\n\n Parameters\n ----------\n incoming_dm : PETSc.DM\n The source DMPlex.\n label_name : str\n Name of the DM label to filter on.\n label_value : int\n Stratum value to select.\n\n Returns\n -------\n PETSc.DM\n The filtered submesh (same dimension as input).\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "petsc_dm_set_regular_refinement", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 166, + "signature": "def petsc_dm_set_regular_refinement(dm, regular=True):", + "parameters": [], + "returns": null, + "existing_docstring": "Flag a DMPlex as having been produced by uniform (regular) refinement\n of its coarse DM.\n\n This is the flag PETSc's geometric multigrid checks\n (``DMCreateInterpolation_Plex``) to take the exact *nested* interpolation\n path (``DMPlexComputeInterpolatorNested``, which maps coarse cell ``c`` to\n fine children ``c*numSubcells + r``) instead of the point-location\n *general* path. Only valid when the dm's point numbering follows the\n canonical ``refine()`` convention and its coarse DM is set\n (``setCoarseDM``). Not exposed by petsc4py, hence this wrapper.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": null, + "is_public": true + }, + { + "name": "petsc_dm_find_labeled_points_local", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 187, + "signature": "def petsc_dm_find_labeled_points_local(dm, label_name, sectionIndex=False, verbose=False):", + "parameters": [], "returns": null, - "existing_docstring": "Use Symbol's atoms() method.", + "existing_docstring": "Identify local points associated with \"Label\"\n\n dm -> expects a petscDM object\n label_name -> \"String Name for Label\"\n sectionIndex -> False: leave points as indexed by the relevant section on the dm\n True: index into the local coordinate array\n\n NOTE: Assumes uniform element types\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_number", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1056, - "signature": "(self)", + "name": "petsc_dm_set_periodicity", + "kind": "function", + "file": "src/underworld3/cython/petsc_discretisation.pyx", + "line": 286, + "signature": "def petsc_dm_set_periodicity(incoming_dm, maxCell, Lstart, L):", "parameters": [], "returns": null, - "existing_docstring": "UWexpression is a Symbol, not a number.", + "existing_docstring": "\n Wrapper for PETSc DMSetPeriodicity:\n - maxCell - Over distances greater than this, we can assume a point has crossed over to another sheet, when trying to localize cell coordinates.\n Pass NULL to remove such information.\n - Lstart - If we assume the mesh is a torus, this is the start of each coordinate, or NULL for 0.0\n - L - If we assume the mesh is a torus, this is the length of each coordinate, otherwise it is < 0.0\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_comparable", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1061, - "signature": "(self)", + "name": "dm_force_coordinate_field", + "kind": "function", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 18, + "signature": "def dm_force_coordinate_field(dm):", "parameters": [], "returns": null, - "existing_docstring": "Delegate to wrapped expression.", + "existing_docstring": "Force coordinate field creation and strip boundary labels from the\n coordinate DM. Must be called after createCoordinateSpace and after\n boundary labels have been added to mesh.dm.\n\n Issue #96: DMClone (inside createCoordinateSpace) copies ALL labels from\n mesh.dm to the coordinate DM. When DMPlexComputeBdIntegral later lazily\n recreates the coordinate field, DMCompleteBCLabels_Internal fails with\n MPI errors on the boundary labels. This function forces the coordinate\n field to be created NOW and strips the labels so the cached field is used\n instead of being lazily recreated.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_extended_real", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1068, - "signature": "(self)", + "name": "evaluate", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 71, + "signature": "def evaluate(self, verbose=False):", "parameters": [], "returns": null, - "existing_docstring": "Delegate to wrapped expression.", + "existing_docstring": "\n Evaluate the integral and return the result with units (if applicable).\n\n Returns\n -------\n float or UWQuantity\n The integral value. If the integrand has units AND the mesh coordinates\n have units, returns a UWQuantity with proper dimensional analysis\n (integrand_units * volume_units). Otherwise returns a plain float.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_positive", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1075, - "signature": "(self)", + "name": "evaluate", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 262, + "signature": "def evaluate(self):", "parameters": [], "returns": null, - "existing_docstring": "Delegate to wrapped expression.", + "existing_docstring": "\n Evaluate the cell-wise integral and return results per cell.\n\n Returns\n -------\n ndarray\n Array of integral values, one per (rank-local) mesh cell.\n\n Raises\n ------\n RuntimeError\n If the mesh has no variables (PETSc limitation).\n If the integrand is a Vector or Dyadic (not supported).\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_negative", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1082, - "signature": "(self)", + "name": "evaluate", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 399, + "signature": "def evaluate(self, verbose=False):", "parameters": [], "returns": null, - "existing_docstring": "Delegate to wrapped expression.", + "existing_docstring": "\n Evaluate the boundary integral and return the result.\n\n Returns\n -------\n float or UWQuantity\n The boundary integral value. If the integrand has units AND the\n mesh coordinates have units, returns a UWQuantity with proper\n dimensional analysis (integrand_units * coord_units^(dim-1)).\n Otherwise returns a plain float.\n", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_zero", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1089, - "signature": "(self)", + "name": "allocate", + "kind": "method", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 24, + "signature": "cpdef allocate(self, int n_res, int n_bcs, int n_jac, int n_bd_res, int n_bd_jac):", "parameters": [], "returns": null, - "existing_docstring": "Delegate to wrapped expression.", + "existing_docstring": "Allocate function pointer arrays of the given sizes.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_finite", - "kind": "property", - "file": "src/underworld3/function/expressions.py", - "line": 1096, - "signature": "(self)", + "name": "copy_residual_from", + "kind": "method", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 40, + "signature": "cpdef copy_residual_from(self, int dst, PtrContainer src, int src_idx):", "parameters": [], "returns": null, - "existing_docstring": "Delegate to wrapped expression.", + "existing_docstring": "Copy a residual function pointer from another container.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_constant", + "name": "copy_bcs_from", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1102, - "signature": "(self, *wrt, **flags)", - "parameters": [ - { - "name": "*wrt", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**flags", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 44, + "signature": "cpdef copy_bcs_from(self, int dst, PtrContainer src, int src_idx):", + "parameters": [], "returns": null, - "existing_docstring": "SymPy-compatible is_constant - delegate to Symbol.", + "existing_docstring": "Copy a BC function pointer from another container.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "is_uw_constant", + "name": "copy_jacobian_from", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1106, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 48, + "signature": "cpdef copy_jacobian_from(self, int dst, PtrContainer src, int src_idx):", "parameters": [], "returns": null, - "existing_docstring": "UW-specific: does this have no mesh variable dependencies?", + "existing_docstring": "Copy a Jacobian function pointer from another container.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "constant", + "name": "copy_bd_residual_from", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1110, - "signature": "(self)", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 52, + "signature": "cpdef copy_bd_residual_from(self, int dst, PtrContainer src, int src_idx):", "parameters": [], "returns": null, - "existing_docstring": "Deprecated - use is_uw_constant().", + "existing_docstring": "Copy a boundary residual function pointer from another container.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "diff", + "name": "copy_bd_jacobian_from", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1114, - "signature": "(self, *args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 56, + "signature": "cpdef copy_bd_jacobian_from(self, int dst, PtrContainer src, int src_idx):", + "parameters": [], "returns": null, - "existing_docstring": "Differentiation - delegate to Symbol.", + "existing_docstring": "Copy a boundary Jacobian function pointer from another container.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": true }, { - "name": "doit", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1684, - "signature": "(self)", + "name": "dim", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1092, + "signature": "(self) -> int", "parameters": [], - "returns": null, - "existing_docstring": "Evaluate the derivative.\n\nReturns\n-------\nsympy.Basic\n The result of differentiating the expression.", + "returns": "int", + "existing_docstring": "Topological dimension of the mesh.\n\nReturns\n-------\nint\n The mesh dimension (2 for 2D, 3 for 3D).", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWDerivativeExpression", + "parent_class": "Mesh", "is_public": true }, { - "name": "mesh_vars_in_expression", - "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 1702, - "signature": "(expr)", - "parameters": [ - { - "name": "expr", - "type_hint": null, - "default": null, - "description": "" - } + "name": "cdim", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1103, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Coordinate dimension (embedding space dimension).\n\nFor most meshes, ``cdim == dim``. For surface meshes embedded in 3D\n(e.g., a 2D spherical shell), ``dim=2`` but ``cdim=3``.\n\nReturns\n-------\nint\n The coordinate dimension.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" ], - "returns": null, - "existing_docstring": "Find all mesh variables and derivatives in an expression.\n\nTraverses the expression tree to find:\n- Regular mesh variable functions (UnderworldAppliedFunction)\n- Derivative functions (UnderworldAppliedFunctionDeriv), grouped by source variable\n\nReturns:\n tuple: (mesh, regular_vars, derivative_vars)\n - mesh: Common mesh for all variables (None if no mesh vars)\n - regular_vars: set of UnderworldAppliedFunction objects\n - derivative_vars: dict mapping source MeshVariable -> list of (deriv_expr, diffindex)\n where diffindex is 0=x, 1=y, 2=z derivative direction", - "harvested_comments": [ - "source_meshvar -> [(deriv_expr, diffindex), ...]", - "Check for derivative functions - collect instead of raising error", - "Don't recurse into derivative args - they are just coordinates", - "Check derivatives FIRST - they inherit from UnderworldAppliedFunction", - "so must be checked before the parent class" + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "element", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1117, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Element type information for the mesh.\n\nContains details about the finite element discretization including\ncell type, polynomial degree, and quadrature order.\n\nReturns\n-------\ndict\n Element information dictionary.\n\nNotes\n-----\nUW3 does not support mixed-element meshes; this applies uniformly\nto all cells.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "length_scale", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1137, + "signature": "(self) -> float", + "parameters": [], + "returns": "float", + "existing_docstring": "Length scale for non-dimensionalization.\n\nThis property is IMMUTABLE after mesh creation to ensure synchronization\nwith all spatial operators (gradient, divergence, curl, etc.).\n\nThe length scale is derived from model reference quantities at mesh creation:\n- Priority 1: `domain_depth` from `model.set_reference_quantities()`\n- Priority 2: `length` from `model.set_reference_quantities()`\n- Default: 1.0 (no scaling)\n\nReturns\n-------\nfloat\n Length scale value for non-dimensionalization\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_depth=uw.quantity(100, \"km\"))\n>>> mesh = uw.meshing.UnstructuredSimplexBox(...)\n>>> mesh.length_scale\n100000.0 # meters\n\nSee Also\n--------\nlength_units : Units string for length scale", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "evaluate", - "kind": "function", - "file": "src/underworld3/function/functions_unit_system.py", - "line": 33, - "signature": "(expr, coords, coord_sys = None, other_arguments = None, simplify = True, verbose = False, evalf = False, mode = 'default', data_layout = None, check_extrapolated = False, smoothing = 1e-06, rbf = None, force_l2 = None)", + "name": "length_units", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1168, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Unit string for the length scale.\n\nReturns\n-------\nstr\n Units for the length scale (e.g., \"meter\", \"kilometer\")\n\nExamples\n--------\n>>> mesh.length_units\n'kilometer'", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "quality", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1184, + "signature": "(self, per_cell = False)", "parameters": [ { - "name": "expr", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "coords", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "coord_sys", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "other_arguments", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "simplify", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "mode", - "type_hint": null, - "default": "'default'", - "description": "" - }, - { - "name": "data_layout", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "check_extrapolated", + "name": "per_cell", "type_hint": null, "default": "False", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Cell-quality diagnostics relevant to FE / solver conditioning.\n\nBulk volume ratios (min/mean) hide the handful of\nnear-degenerate cells that nonetheless dominate\nstiffness-matrix conditioning \u2014 a Stokes / saddle-point\nsolve line-search-fails on the *worst* element, not the\nmean. This reports the tail metrics that actually predict\nthat. For a 2-D simplex (triangle) mesh, per cell:\n\n* shape quality ``q = 4\u221a3\u00b7A / \u03a3\u2113\u00b2`` (1 = equilateral,\n \u2192 0 = sliver; folds skew + stretch into one number)\n* largest interior angle (\u2192 180\u00b0 is the conditioning killer)\n* aspect ratio ``\u2113_max\u00b2 / (2A)`` (longest edge / shortest\n altitude)\n* neighbour size-jump (adjacent-cell area ratio \u2014 the mesh\n gradation the solver actually sees)\n\nThe conditioning-relevant numbers are the *worst* cell\n(``q_min``, ``angle_max_deg``, ``aspect_max``) and the\npoor-cell counts, not the means. Non-2-D-simplex meshes get\nthe dimension-agnostic cell-volume-spread subset only.\n\nParameters\n----------\nper_cell : bool, default False\n Also return per-cell arrays (``q``, ``angle_deg``,\n ``aspect``, ``volume``) under ``\"per_cell\"`` \u2014 for\n plotting or locating the bad cells.\n\nReturns\n-------\ndict\n Aggregate + tail stats. Headline scalars (min/max/counts)\n are MPI-reduced so they are correct in parallel;\n percentiles and the neighbour size-jump are rank-local\n estimates (exact in serial \u2014 the convention for the\n mesh-redistribution tooling).\n\nExamples\n--------\n>>> q = mesh.quality()\n>>> q[\"q_min\"], q[\"n_q_lt_0p3\"], q[\"aspect_max\"]\n>>> mesh.quality(per_cell=True)[\"per_cell\"][\"q\"] # to plot", + "harvested_comments": [ + "2-D triangle area from the z-component of the edge cross product", + "(numpy 2.0 removed the 2-D np.cross that returned this scalar)." + ], + "status": "complete", + "needs": [], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1337, + "signature": "(self, level = 0)", + "parameters": [ { - "name": "smoothing", + "name": "level", "type_hint": null, - "default": "1e-06", + "default": "0", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Displays mesh information at different levels.\n\nParameters\n----------\nlevel : int (0 default)\n The display level.\n 0, for basic mesh information (variables and boundaries), while level=1 displays detailed mesh information (including PETSc information)", + "harvested_comments": [ + "{self.instance}: {self.name}\\n\")", + "Display coordinate units if set", + "Display length scale for non-dimensionalization", + "Display coordinate system information", + "Show available coordinate accessors" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "view_parallel", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1556, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "returns the break down of boundary labels from each processor", + "harvested_comments": [ + "{self.instance}: {self.name}\\n\")", + "# Boundary information on each proc", + "## goes through each processor and gets the label size" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "clone_dm_hierarchy", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1607, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Clone the dm hierarchy on the mesh", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "extract_region", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1623, + "signature": "(self, label_name, label_value = None)", + "parameters": [ { - "name": "rbf", + "name": "label_name", "type_hint": null, - "default": "None", + "default": null, "description": "" }, { - "name": "force_l2", + "name": "label_value", "type_hint": null, "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Evaluate expression at coordinates with automatic unit handling.\n\nThis function wraps the Cython evaluate_nd implementation to automatically\nhandle unit conversions and return unit-aware results.\n\nParameters\n----------\nexpr : sympy expression or UWexpression\n Expression to evaluate\ncoords : array-like\n Coordinates at which to evaluate. Can be:\n - numpy array of doubles (shape: n_points x n_dims) in non-dimensional form\n - UnitAwareArray with dimensional coordinates (e.g., from mesh.X.coords)\n - Both work transparently - dimensional coords are auto-converted\ncoord_sys : mesh.N vector coordinate system, optional\n Coordinate system to use (default: None)\nother_arguments : dict, optional\n Additional arguments for evaluation (default: None)\nsimplify : bool, optional\n Whether to simplify expression (default: True)\nverbose : bool, optional\n Verbose output (default: False)\nevalf : bool, optional\n Force numerical evaluation via sympy evalf (default: False)\nmode : str, optional\n Evaluation mode controlling accuracy vs speed tradeoff:\n - \"default\": Accurate evaluation. Projection for derivatives (O(h\u00b2)),\n direct calculation otherwise. DMInterpolation inside mesh, RBF outside.\n - \"fast\": Quick visualization mode. Clement gradient recovery for\n derivatives (O(h), no solve), RBF interpolation everywhere.\n - \"projection\": Always use L2 projection (even without derivatives),\n DMInterpolation inside mesh, RBF outside.\n Default: \"default\"\ndata_layout : callable, optional\n Data layout specification (default: None)\ncheck_extrapolated : bool, optional\n Check for extrapolated values (default: False)\nsmoothing : float, optional\n Smoothing parameter for L2 projection (dimensionless).\n Only used when projection is active. Default: 1e-6\nrbf : bool, optional\n Expert override: Force RBF interpolation everywhere. Overrides mode.\nforce_l2 : bool, optional\n Expert override: Force L2 projection path. Overrides mode.\n\nReturns\n-------\nUWQuantity, UnitAwareArray, or ndarray\n - If non-dimensional scaling is active: plain ndarray (non-dimensional)\n - If expression has units and result is scalar: UWQuantity\n - If expression has units and result is array: UnitAwareArray\n - If expression has no units: plain ndarray (as before)\n\nNotes\n-----\n**Evaluation Modes:**\n\n| Mode | Derivatives | No Derivatives | Interpolation |\n|------|-------------|----------------|---------------|\n| \"fast\" | Clement (no solve) | Calculation | RBF everywhere |\n| \"default\" | Projection (solve) | Calculation | DMInterp + RBF |\n| \"projection\" | Projection (solve) | Projection | DMInterp + RBF |\n\nThe `rbf` and `force_l2` parameters are expert overrides that take\nprecedence over the mode setting when explicitly provided.\n\nExamples\n--------\n>>> # Works with both dimensional and non-dimensional coords\n>>> result = uw.function.evaluate(T.sym, T.coords) # dimensional coords\n>>> result = uw.function.evaluate(T.sym, mesh.data[:, :2]) # non-dimensional\n>>> if hasattr(result, 'to'):\n... result_K = result.to('K') # Unit conversion", + "existing_docstring": "Extract a submesh containing only cells with the given region label.\n\nUses ``DMPlexFilter`` to create a new mesh sharing exact node\npositions with the parent. The submesh carries a ``subpoint_is``\nmapping back to the parent for restrict/prolongate operations,\nand a ``parent`` reference.\n\nBoundary labels from the parent survive the filter. For example,\nan \"Internal\" boundary on the parent becomes an exterior boundary\non the submesh and can be referenced by the same name.\n\nParameters\n----------\nlabel_name : str\n DM label name identifying the region (e.g., ``\"Inner\"``).\nlabel_value : int, optional\n Stratum value within the label. If ``None``, uses\n ``mesh.regions..value`` when available.\n\nReturns\n-------\nMesh\n A new mesh covering only the specified region.\n\nExamples\n--------\n>>> full_mesh = uw.meshing.AnnulusInternalBoundary(...)\n>>> rock_mesh = full_mesh.extract_region(\"Inner\")\n>>> rock_mesh.parent is full_mesh\nTrue", "harvested_comments": [ - "Expert overrides (override mode settings)", - "Works with both dimensional and non-dimensional coords", - "dimensional coords", - "non-dimensional", - "Unit conversion" + "Resolve label value", + "Filter the DM", + "Build boundaries enum from labels that survived the filter", + "(DMPlexFilter preserves parent labels on the submesh)", + "Get the subpoint IS before wrapping (the Mesh constructor may modify the DM)" ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "global_evaluate", - "kind": "function", - "file": "src/underworld3/function/functions_unit_system.py", - "line": 368, - "signature": "(expr, coords = None, coord_sys = None, other_arguments = None, simplify = True, verbose = False, evalf = False, mode = 'default', data_layout = None, check_extrapolated = False, smoothing = 1e-06, rbf = None, force_l2 = None)", + "name": "extract_surface", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1733, + "signature": "(self, label_name, label_value = None, verbose = False)", "parameters": [ { - "name": "expr", + "name": "label_name", "type_hint": null, "default": null, "description": "" }, { - "name": "coords", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "coord_sys", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "other_arguments", + "name": "label_value", "type_hint": null, "default": "None", "description": "" }, - { - "name": "simplify", - "type_hint": null, - "default": "True", - "description": "" - }, { "name": "verbose", "type_hint": null, "default": "False", "description": "" - }, - { - "name": "evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "mode", - "type_hint": null, - "default": "'default'", - "description": "" - }, - { - "name": "data_layout", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "check_extrapolated", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "smoothing", - "type_hint": null, - "default": "1e-06", - "description": "" - }, - { - "name": "rbf", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "force_l2", - "type_hint": null, - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": "Global evaluate with automatic unit-aware results.\n\nSimilar to evaluate() but performs global evaluation across all processes.\nReturns unit-aware objects when expression has units.\n\nParameters\n----------\nexpr : sympy expression or UWexpression\n Expression to evaluate\ncoords : array-like, optional\n Coordinates at which to evaluate (default: None)\ncoord_sys : CoordinateSystem, optional\n Coordinate system to use (default: None)\nother_arguments : dict, optional\n Additional arguments for evaluation (default: None)\nsimplify : bool, optional\n Whether to simplify expression (default: True)\nverbose : bool, optional\n Verbose output (default: False)\nevalf : bool, optional\n Force numerical evaluation via sympy evalf (default: False)\nmode : str, optional\n Evaluation mode controlling accuracy vs speed tradeoff:\n - \"default\": Accurate evaluation. Projection for derivatives (O(h\u00b2)),\n direct calculation otherwise. DMInterpolation inside mesh, RBF outside.\n - \"fast\": Quick visualization mode. Clement gradient recovery for\n derivatives (O(h), no solve), RBF interpolation everywhere.\n - \"projection\": Always use L2 projection (even without derivatives),\n DMInterpolation inside mesh, RBF outside.\n Default: \"default\"\ndata_layout : callable, optional\n Data layout specification (default: None)\ncheck_extrapolated : bool, optional\n Check for extrapolated values (default: False)\nsmoothing : float, optional\n Smoothing parameter for L2 projection (dimensionless).\n Only used when projection is active. Default: 1e-6\nrbf : bool, optional\n Expert override: Force RBF interpolation everywhere. Overrides mode.\nforce_l2 : bool, optional\n Expert override: Force L2 projection path. Overrides mode.\n\nReturns\n-------\nUWQuantity, UnitAwareArray, or ndarray\n - If non-dimensional scaling is active: plain ndarray (non-dimensional)\n - Otherwise: result with appropriate unit tracking\n\nNotes\n-----\nSee :func:`evaluate` for details on evaluation modes.", + "existing_docstring": "Extract the codimension-1 surface marked by ``label_name`` as a mesh.\n\nThe third submesh flavour (alongside :meth:`extract_region`, which\nfilters cells of the *same* dimension): a *surface submesh* is a real\n:class:`Mesh` for the parent's codim-1 boundary stratum, sharing exact\nvertex positions with the parent. On a 3D ``SphericalShell``,\n``shell.extract_surface(\"Upper\")`` returns a 2-manifold embedded in\n3-space (``dim = parent.dim - 1``, ``cdim = parent.cdim``).\n\nMechanism: ``DMPlexCreateSubmesh`` on the face label produces a cd-1\nDM but retains an upward-DAG phantom stratum (one point per parent\nvolume cell) that breaks closure-based navigation; ``DMPlexFilter`` on\n``(depth, dim-1)`` strips it, leaving a clean standalone manifold. The\ntwo subpoint IS's compose into a single surface\u2192parent point map.\n\nParent \u2194 submesh DOF transfer reuses :meth:`restrict` / :meth:`prolongate`\n(the same KDTree coordinate-match-at-1e-10 path as ``extract_region``);\nsurface vertices are an *exact* subset of the parent's, so it is\nbit-exact.\n\nParameters\n----------\nlabel_name : str\n Name of the parent boundary label whose marked faces become the\n cells of the surface submesh (e.g. ``\"Upper\"``).\nlabel_value : int, optional\n Stratum value within the label. If ``None``, resolved from\n ``self.boundaries[label_name].value``.\n\nReturns\n-------\nMesh\n A surface mesh with ``parent`` set to this mesh and the standard\n submesh lineage (``subpoint_is``, registration with\n ``_registered_submeshes``).\n\nRaises\n------\nValueError\n If ``label_name`` is missing from the parent or its face stratum\n is empty (loud-fail contract \u2014 no degenerate-mesh fallback).", "harvested_comments": [ - "Expert overrides (override mode settings)", - "Map mode to internal flags (rbf, force_l2)", - "Expert overrides take precedence when explicitly provided", - "Use mode settings", - "Expert overrides - use provided values or defaults" + "NB: never call getStratumIS(v) for a value not in the live value set", + "\u2014 that hard-aborts PETSc on some labels (cf. the \"Centre\" pseudo-", + "label). Probe getValueIS() first.", + "Stage 1: cd-1 DM (with the phantom upward-DAG stratum).", + "Stage 2: strip the phantom \u2014 keep only the surface cells (height-0" ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "evaluate_gradient", - "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 48, - "signature": "(scalar_var, coords, method = 'interpolant', component = None)", + "name": "sync_coordinates_from_parent", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1949, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Update submesh coordinates from the parent mesh.\n\nCalled automatically when the parent mesh deforms. Uses the\ncached vertex map to copy parent vertex positions to the\nsubmesh, then calls ``_deform_mesh`` to rebuild geometry.\n\nRaises\n------\nValueError\n If this mesh has no parent.", + "harvested_comments": [ + "Submesh follows its parent's geometry; this is a sanctioned", + "internal coordinate move (the parent owns the transfer)." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "restrict", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2155, + "signature": "(self, parent_var, sub_var, mode = 'replace')", "parameters": [ { - "name": "scalar_var", + "name": "parent_var", "type_hint": null, "default": null, "description": "" }, { - "name": "coords", + "name": "sub_var", "type_hint": null, "default": null, "description": "" }, { - "name": "method", - "type_hint": null, - "default": "'interpolant'", - "description": "" - }, - { - "name": "component", + "name": "mode", "type_hint": null, - "default": "None", + "default": "'replace'", "description": "" } ], "returns": null, - "existing_docstring": "Evaluate gradient of a mesh variable at arbitrary coordinates.\n\nComputes the gradient of a MeshVariable (or one of its components) and\nevaluates it at the specified coordinates.\n\nParameters\n----------\nscalar_var : MeshVariable\n Field to compute gradient of. Can be scalar (num_components=1) or\n vector/tensor field. For multi-component fields, use `component`\n parameter to specify which component's gradient to compute.\ncoords : array-like\n Coordinates at which to evaluate gradient, shape (n_points, dim).\n Can be numpy array or UnitAwareArray.\nmethod : str, optional\n Gradient computation method:\n - \"interpolant\": Clement interpolant (O(h) accurate, no solve). Default.\n - \"projection\": L2 projection (O(h\u00b2) accurate, requires solve).\ncomponent : int or None, optional\n For multi-component fields, which component to compute gradient of.\n If None and field has multiple components, raises ValueError.\n For scalar fields, this parameter is ignored.\n\nReturns\n-------\nndarray\n Gradient values at requested coordinates, shape (n_points, dim).\n gradient[i, j] = \u2202f/\u2202x\u2c7c at coords[i].\n\nNotes\n-----\n**Interpolant (Clement) Method**: Uses PETSc's\n`DMPlexComputeGradientClementInterpolant` which averages cell-wise gradients\nat vertices. This is O(h) accurate - error halves when mesh resolution doubles.\nFast but limited to first-order accuracy.\n\n**Projection (L2) Method**: Solves a mass matrix system to find the optimal\nL2 projection of the gradient onto the finite element space. This is O(h\u00b2)\naccurate for smooth solutions. The projection is cached on the mesh for\nrepeated calls, using the previous solution as initial guess.\n\nExamples\n--------\n>>> mesh = uw.meshing.StructuredQuadBox(elementRes=(16, 16))\n>>> T = uw.discretisation.MeshVariable('T', mesh, 1)\n>>> T.array[:, 0, 0] = T.coords[:, 0]**2 # T = x\u00b2\n>>>\n>>> # Fast gradient (O(h))\n>>> grad_fast = evaluate_gradient(T, coords, method=\"interpolant\")\n>>>\n>>> # Accurate gradient (O(h\u00b2))\n>>> grad_accurate = evaluate_gradient(T, coords, method=\"projection\")\n\nSee Also\n--------\nuw.systems.Projection : Direct L2 projection for explicit control\nuw.function.evaluate : General function evaluation\n\nReferences\n----------\n.. [1] Cl\u00e9ment, P. (1975). \"Approximation by finite element functions\n using local regularization\". RAIRO Analyse num\u00e9rique, 9(R-2), 77-84.", + "existing_docstring": "Copy data from a parent-mesh variable to a submesh variable.\n\nParameters\n----------\nparent_var : MeshVariable\n Source variable on the parent mesh.\nsub_var : MeshVariable\n Destination variable on this (sub)mesh.\nmode : str\n ``\"replace\"`` overwrites submesh values (INSERT_VALUES).\n ``\"add\"`` adds parent values into submesh (ADD_VALUES).\n\nRaises\n------\nValueError\n If this mesh has no parent, or the variable meshes don't match.", "harvested_comments": [ - "Fast gradient (O(h))", - "Accurate gradient (O(h\u00b2))" + "Copy, modify, then write through pack_raw_data_to_petsc", + "to properly sync the PETSc Vec without callback issues" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "interpolate_gradients_at_coords", - "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 493, - "signature": "(source_vars, coords, mesh, method = 'interpolant')", + "name": "prolongate", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2195, + "signature": "(self, sub_var, parent_var, mode = 'replace')", "parameters": [ { - "name": "source_vars", + "name": "sub_var", "type_hint": null, "default": null, "description": "" }, { - "name": "coords", + "name": "parent_var", "type_hint": null, "default": null, "description": "" }, { - "name": "mesh", + "name": "mode", "type_hint": null, - "default": null, + "default": "'replace'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Copy data from a submesh variable to a parent-mesh variable.\n\nParameters\n----------\nsub_var : MeshVariable\n Source variable on this (sub)mesh.\nparent_var : MeshVariable\n Destination variable on the parent mesh.\nmode : str\n ``\"replace\"`` overwrites parent values at submesh DOFs.\n ``\"add\"`` adds submesh values into parent.\n\nRaises\n------\nValueError\n If this mesh has no parent, or the variable meshes don't match.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "nuke_coords_and_rebuild", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2235, + "signature": "(self, verbose = False, active_vars = None)", + "parameters": [ + { + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "method", + "name": "active_vars", "type_hint": null, - "default": "'interpolant'", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Compute gradients for multiple source variables and interpolate.\n\nComputes gradients for each source variable using the specified method\nand evaluates at the specified coordinates.\n\nParameters\n----------\nsource_vars : list of MeshVariable or list of (MeshVariable, int) tuples\n Fields needing gradient computation. For scalar fields, just pass\n the MeshVariable. For multi-component fields, pass tuples of\n (MeshVariable, component_index).\ncoords : array-like\n Coordinates at which to evaluate gradients, shape (n_points, dim).\nmesh : Mesh\n The mesh containing the variables.\nmethod : str, optional\n Gradient computation method:\n - \"interpolant\": Clement interpolant (O(h) accurate, no solve). Default.\n - \"projection\": L2 projection (O(h\u00b2) accurate, requires solve).\n\nReturns\n-------\ndict\n Mapping from (var, component) tuple to gradient array (n_points, dim).\n For scalar fields, component is 0.\n result[(var, component)][i, j] = \u2202var[component]/\u2202x\u2c7c at coords[i].\n\nNotes\n-----\nFor an expression with multiple derivatives like `T.diff(x) + v[0].diff(y)`:\n- Identifies source variables and components: {(T, 0), (v, 0)}\n- Computes gradient once per (variable, component) pair\n- Each derivative component extracted from the appropriate gradient\n\nThis is called internally by evaluate_nd when derivatives are detected.", + "existing_docstring": "Rebuild DM/DS, the kd-tree, mesh sizes, and per-variable DOF\ncoordinate caches after a coordinate change.\n\n``active_vars`` (optional set/list of MeshVariables): restrict\nthe per-variable DOF coordinate-cache recomputation to this set.\nWhen ``None`` (default) every registered variable is\nrecomputed eagerly, matching the BUGFIX(#130) collective-safe\nbehaviour. Movers that thread their own work-vars through\n``_deform_mesh(..., active_vars=...)`` skip recomputing the\nnon-mover variables n_outer\u00d7 during the inner sweep; the\nwrapper does one final ``_deform_mesh`` (or a direct\n``nuke_coords_and_rebuild()``) with ``active_vars=None`` at\nsweep exit to bring the full cache back into sync.\n\nNaming note: \"nuke and rebuild\" historically referred to the\nDS+DM tear-down/recreate; what was called \"refill\" of the\nper-variable cache (line 1890 in the old code) is in fact\n*recomputation* of each variable's DOF coordinates from the new\nmesh coordinates \u2014 the storage is per-variable, the values are\nderived. The ``active_vars`` whitelist controls which of those\nrecomputations runs now versus deferring to the next full\nrebuild.", "harvested_comments": [ - "Normalize input: convert bare variables to (var, component) tuples", - "Bare variable - assume scalar (component 0)", - "For multi-component fields, caller should specify components", - "Multi-component field passed without component spec", - "Compute gradient for all components" + "130) collective-safe", + "This is a reversion to the old version (3.15 compatible which seems to work in 3.16 too)", + "let's go ahead and do an initial projection from linear (the default)", + "to linear. this really is a nothing operation, but a", + "side effect of this operation is that coordinate DM DMField is" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "boundary_normal", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2504, + "signature": "(self, boundary)", + "parameters": [ + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Outward unit normal of a single boundary, tracking deformation.\n\nAssembles the EXACT, outward, area-weighted PETSc facet normals\n(``dm.computeCellGeometryFVM``) from ONLY this boundary's facets onto\nits P1 vertices. Because each boundary is assembled independently,\na vertex shared by two boundaries (a sharp corner) is NOT averaged\nacross the discontinuity \u2014 each boundary keeps its own normal. On a\nsmooth boundary (e.g. a free surface) the result is the smooth\ndeformed normal. Cached per boundary; rebuilt lazily after a deform.\n\nReturns a sympy Matrix (row) of the P1 normal-field components, for\nuse as the constraint direction in Nitsche/penalty BCs.\n\nParameters\n----------\nboundary : str or enum\n Boundary label name (or a ``mesh.boundaries`` enum member).", + "harvested_comments": [], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "compute_clement_gradient_at_nodes", - "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 570, - "signature": "(var, component = None)", + "name": "cell_size", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2612, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Local, per-cell characteristic mesh size as a scalar field symbol.\n\nReturns the ``.sym`` of a cell-constant (degree-0, discontinuous)\nscalar MeshVariable holding each cell's characteristic length (the\n``volume**(1/dim)`` equivalent radius, i.e. ``self._radii``). Unlike\nthe single *global* scalar from :meth:`get_min_radius` (the smallest\ncell anywhere), this varies cell to cell, so a stabilisation that\nscales as :math:`1/h` \u2014 e.g. the Nitsche free-slip penalty\n:math:`\\gamma\\mu/h` \u2014 is correctly scaled on every facet of a\nnon-uniform or adaptively-refined mesh rather than using the global\nminimum (which over-penalises coarse cells and drifts as refinement\nchanges the global min).\n\nOn a boundary integral the kernel sees the value of the cell adjacent\nto the facet. The field is cached and rebuilt lazily; its data is\nrefreshed when the mesh deforms or is adapted (see :meth:`deform`),\nso it tracks a moving / re-refined mesh \u2014 a stale size on a deformed\nmesh would re-introduce the mis-scaling.\n\nOn a uniform mesh every cell is the same size, so this reduces to the\nglobal ``get_min_radius`` value everywhere and existing behaviour is\npreserved to tolerance.\n\nReturns\n-------\nsympy scalar\n The cell-size field symbol, for use in JIT-compiled residuals.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "Gamma_P1", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2698, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Projected P1 boundary normals as a sympy Matrix.\n\nReturns the normalised, vertex-averaged PETSc face normals\nas a smooth P1 field. Preferred over :attr:`Gamma_N` for\npenalty and Nitsche BCs on curved boundaries \u2014 gives\nconsistent orientation and better convergence in 3D.\n\nAutomatically updated when the mesh deforms.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "bounding_surfaces", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2718, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mapping ``{label: BoundingSurface}`` of this mesh's registered\nbounding-surface objects (tangent-slip + restore).\n\nThis is a NEW collection, separate from and additional to\n:attr:`boundaries` (the persisted gmsh/DMPlex label ``Enum``, left\nuntouched). Populated by analytic-geometry constructors (Annulus,\nSphericalShell, CubedSphere, box meshes); user-extendable via\n:meth:`register_tangent_slip_provider`.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "register_tangent_slip_provider", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2732, + "signature": "(self, label, surface)", "parameters": [ { - "name": "var", + "name": "label", "type_hint": null, "default": null, "description": "" }, { - "name": "component", + "name": "surface", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Compute Clement gradient at mesh nodes only (no interpolation).\n\nThis is the raw Clement interpolant without arbitrary point evaluation.\nUseful when you only need values at mesh nodes, e.g., for error estimation\nor visualization at node locations.\n\nParameters\n----------\nvar : MeshVariable\n Field to compute gradient of. Can be scalar or multi-component.\ncomponent : int or None, optional\n For multi-component fields, which component to compute gradient of.\n If None and field is scalar, uses the only component.\n If None and field is multi-component, raises ValueError.\n\nReturns\n-------\nndarray\n Gradient at mesh nodes, shape (n_nodes, dim).\n gradient[i, j] = \u2202f/\u2202x\u2c7c at node i.\n\nNotes\n-----\nUses PETSc's `DMPlexComputeGradientClementInterpolant` which computes\nthe L2 projection of cell-wise constant gradients onto a continuous\nP1 (linear) space by averaging at shared vertices.\n\nThis is O(h) accurate - suitable for error estimation, quick visualization,\nor when higher accuracy is not required.\n\nFor higher-degree fields (P2, etc.), the function first samples the field\nat P1 vertex locations before computing the Clement gradient.\n\nExamples\n--------\n>>> T.array[:, 0, 0] = T.coords[:, 0]**2 + T.coords[:, 1]**2\n>>> grad = compute_clement_gradient_at_nodes(T)\n>>> # grad[i] \u2248 [2*x_i, 2*y_i] at each node\n\n>>> # For vector field, specify component\n>>> grad_vx = compute_clement_gradient_at_nodes(v, component=0)", - "harvested_comments": [ - "grad[i] \u2248 [2*x_i, 2*y_i] at each node", - "For vector field, specify component", - "Handle multi-component fields", - "Get vertex coordinates", - "Get field values at P1 vertex locations" + "existing_docstring": "Install a :class:`BoundingSurface` object for a boundary ``label``\n(separate from the persisted ``mesh.boundaries`` labelling).\n\nLets a user declare a custom analytic surface (e.g. an ellipsoid) the\nconstructors don't know about, or replace one (free-surface release).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "is_pure_sympy_expression", - "kind": "function", - "file": "src/underworld3/function/pure_sympy_evaluator.py", - "line": 22, - "signature": "(expr)", + "name": "restore_to_surface", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2772, + "signature": "(self, coords, label)", "parameters": [ { - "name": "expr", + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "label", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Detect if an expression contains only pure sympy symbols or mesh coordinates (no UW3 variable data).\n\nExpressions are considered \"pure\" (lambdifiable) if they contain:\n- Only sympy.Symbol objects\n- Only mesh coordinate BaseScalars (mesh.X[0], mesh.X[1], etc.)\n- Mix of Symbols and BaseScalars\n- No UW3 MeshVariable or SwarmVariable data\n\nParameters\n----------\nexpr : sympy expression\n Expression to check\n\nReturns\n-------\nbool\n True if expression can be lambdified without mesh data interpolation\nsymbols : set or None\n Set of free symbols if pure, None otherwise\nsymbol_type : str or None\n 'symbol', 'coordinate', or 'mixed' indicating what symbols were found\n\nExamples\n--------\n>>> import sympy\n>>> x = sympy.Symbol('x')\n>>> t = sympy.Symbol('t')\n>>> is_pure_sympy_expression(x**2 + t)\n(True, {x, t}, 'symbol')\n\n>>> # With mesh coordinates\n>>> x_mesh = mesh.X[0]\n>>> is_pure_sympy_expression(sympy.erf(x_mesh - 0.5))\n(True, {N.x}, 'coordinate')\n\n>>> # With UW3 variable data - NOT pure\n>>> T = uw.discretisation.MeshVariable(\"T\", mesh, 1)\n>>> is_pure_sympy_expression(T.sym[0] + x)\n(False, None, None)", - "harvested_comments": [ - "With mesh coordinates", - "With UW3 variable data - NOT pure", - "Get all free symbols", - "Not a sympy expression", - "Constant expression - can be handled efficiently" + "existing_docstring": "Snap ``coords`` onto the named bounding surface (delegates to the\nsurface object's ``restore``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "get_cached_lambdified", - "kind": "function", - "file": "src/underworld3/function/pure_sympy_evaluator.py", - "line": 145, - "signature": "(expr, symbols, modules = ('scipy', 'numpy'))", + "name": "tangent_project", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2778, + "signature": "(self, coords, label, reference)", "parameters": [ { - "name": "expr", + "name": "coords", "type_hint": null, "default": null, "description": "" }, { - "name": "symbols", + "name": "label", "type_hint": null, "default": null, "description": "" }, { - "name": "modules", + "name": "reference", "type_hint": null, - "default": "('scipy', 'numpy')", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Get a cached lambdified function for an expression.\n\nUses an LRU cache to avoid recompiling the same expression multiple times.\n\nParameters\n----------\nexpr : sympy expression\n Expression to lambdify\nsymbols : tuple of sympy.Symbol\n Symbols in order for lambdify\nmodules : tuple of str, optional\n Modules to use for lambdify. Default: ('scipy', 'numpy')\n scipy is required for special functions like erf, gamma, etc.\n\nReturns\n-------\ncallable\n Lambdified function\n\nNotes\n-----\nCache key is based on:\n- Expression structure (hash of srepr)\n- Symbol names and order\n- Modules used", - "harvested_comments": [ - "Create cache key", - "Check cache", - "Lambdify with scipy for special functions", - "Fallback to numpy only if scipy fails" + "existing_docstring": "Tangent-slide ``coords`` (displacement measured from ``reference``)\non the named bounding surface (delegates to the surface object).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "evaluate_pure_sympy", - "kind": "function", - "file": "src/underworld3/function/pure_sympy_evaluator.py", - "line": 199, - "signature": "(expr, coords, coord_symbols = None)", + "name": "boundary_slip", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2785, + "signature": "(self, slip_spec = True, reference_coords = None, boundary_labels = None)", "parameters": [ { - "name": "expr", + "name": "slip_spec", "type_hint": null, - "default": null, + "default": "True", "description": "" }, { - "name": "coords", + "name": "reference_coords", "type_hint": null, - "default": null, + "default": "None", "description": "" }, { - "name": "coord_symbols", + "name": "boundary_labels", "type_hint": null, "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Fast evaluation of pure sympy expressions using cached lambdified functions.\n\nThis function provides optimized evaluation for expressions containing only\npure sympy symbols (no UW3 MeshVariable symbols). It automatically:\n1. Detects the free symbols in the expression\n2. Maps coordinate columns to symbols\n3. Uses cached lambdified functions for efficiency\n\nParameters\n----------\nexpr : sympy expression\n Pure sympy expression to evaluate\ncoords : np.ndarray\n Coordinates at which to evaluate, shape (n_points, n_dims)\n For 2D: coords[:, 0] are x values, coords[:, 1] are y values\n For 3D: coords[:, 0] are x, coords[:, 1] are y, coords[:, 2] are z\ncoord_symbols : tuple of sympy.Symbol, optional\n Symbols representing coordinates in order (x, y, z)\n If None, will try to infer from expression's free symbols\n\nReturns\n-------\nnp.ndarray\n Evaluated expression values, shape depends on expression:\n - Scalar expr: (n_points,) or (n_points, 1, 1)\n - Vector expr: (n_points, n_components)\n - Matrix expr: (n_points, n_rows, n_cols)\n\nExamples\n--------\n>>> import sympy\n>>> import numpy as np\n>>> x, y = sympy.symbols('x y')\n>>> expr = sympy.sqrt(x**2 + y**2) # Distance from origin\n>>> coords = np.array([[1.0, 0.0], [0.0, 1.0], [3.0, 4.0]])\n>>> result = evaluate_pure_sympy(expr, coords, coord_symbols=(x, y))\n>>> # result = [1.0, 1.0, 5.0]\n\nNotes\n-----\n- Uses scipy for special functions (erf, gamma, etc.)\n- Caches lambdified functions for repeated evaluations\n- ~10,000x faster than sympy.subs() for many points", + "existing_docstring": "Build ``(is_pinned, project)`` for tangent slip on this mesh's\nbounding surfaces \u2014 the orchestrator the metric movers call.\n\nSee ``docs/developer/design/boundary-slip-strategy.md``. The mesh\nclassifies which vertices slip vs pin (the cross-surface concern); each\nsurface object owns its tangent-project + restore.\n\nA vertex **slips** iff it lies on **exactly one** slip surface that has a\nregistered analytic :class:`BoundingSurface`; non-boundary, junction\n(\u22652 surfaces), unregistered-surface, or degenerate-normal vertices are\n**pinned** (the step-1 safe default \u2014 ``facet`` restore is a follow-up).\n\nParameters\n----------\nslip_spec :\n See :meth:`_resolve_slip_spec`. Default ``True`` (all surfaces).\nreference_coords : ndarray, optional\n Fixed reference vertex positions (local-chart vertex order) that\n displacements are measured from. Defaults to ``mesh.X.coords``.\nboundary_labels : iterable of str, optional\n Boundary labels defining the boundary (``is_bnd``). Defaults to all\n geometric boundary labels; pass a mover's pinned set for parity.\n\nReturns\n-------\n(is_pinned, project) : (ndarray[bool], callable)\n ``is_pinned`` is the per-vertex pinned mask (local-chart order);\n ``project(Y)`` slides+restores the slip vertices of ``Y`` in place\n and returns it.", "harvested_comments": [ - "Distance from origin", - "result = [1.0, 1.0, 5.0]", - "Ensure coords is 2D numpy array", - "Handle Matrix expressions", - "For matrices, we'll evaluate element-wise and reshape" + "TODO(follow-up): _pinned_mask expands labels through vertices/edges", + "only, so a 3D boundary label that tags FACES alone (a mesh loaded with", + "markVertices=False) leaves its boundary vertices unmarked. This is a", + "pre-existing limitation of the shared helper used by every mover; the", + "fix (close faces\u2192edges\u2192vertices) belongs with _pinned_mask itself." ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "clear_lambdify_cache", - "kind": "function", - "file": "src/underworld3/function/pure_sympy_evaluator.py", - "line": 426, - "signature": "()", + "name": "project_to_slip_surface", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2926, + "signature": "(self, coords, slip_spec = True, reference_coords = None, boundary_labels = None)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "slip_spec", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "reference_coords", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "boundary_labels", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "In-place convenience over :meth:`boundary_slip`: slide+restore the\nslip vertices of ``coords`` (a full local-chart vertex array) and return\nit. For callers that just want coordinates snapped back (a checkpoint\nreload, a diagnostic, the free-surface module).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "update_lvec", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2938, + "signature": "(self, swarm_sync = True)", + "parameters": [ + { + "name": "swarm_sync", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "This method creates and/or updates the mesh variable local vector.\nIf the local vector is already up to date, this method will do nothing.\n\n``swarm_sync=False`` skips the swarm-dependency hook below. It MUST\nbe passed at call sites that only a SUBSET of ranks reach (e.g. the\nrefresh calls inside ``petsc_interpolate`` \u2014 ranks with zero interior\npoints skip that function entirely): the hook performs collective\nreductions, so running it on a subset of ranks deadlocks. Such sites\nrely on an earlier all-ranks ``update_lvec()`` for freshness, exactly\nas they already do for the collective ``globalToLocal`` below.", + "harvested_comments": [ + "Swarm dependencies first (issues #215 Bug 3 / #289): run any", + "deferred particle migration and refresh stale swarm-variable", + "proxies BEFORE the staleness check below \u2014 the refresh writes", + "proxy MeshVariable data, which is what sets _stale_lvec. Solvers", + "read the proxy DM directly, so the lazy `.sym` refresh alone" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "lvec", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3004, + "signature": "(self) -> PETSc.Vec", "parameters": [], + "returns": "PETSc.Vec", + "existing_docstring": "Returns a local Petsc vector containing the flattened array\nof all the mesh variables.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "register_remesh_hook", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3017, + "signature": "(self, op)", + "parameters": [ + { + "name": "op", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Clear the cached lambdified functions.\n\nUseful for testing or if memory usage becomes a concern.", + "existing_docstring": "Register an operator's ``on_remesh(ctx)`` callback.\n\nCalled by the adapt op (``smooth_mesh_interior``, ``OT_adapt``,\n``follow_metric``) after the generic per-variable REMAP pass.\n``op`` must expose an ``on_remesh(ctx)`` method; ``ctx`` is a\n:class:`~underworld3.discretisation.remesh.RemeshContext` with\nthe old/new coords, total displacement, ``dt``, and a scratch\ndict for stashing things like ``v_mesh`` for the next solve.\n\nStored as a weak reference so operators that go out of scope are\ncleaned up automatically. Idempotent: registering the same\noperator twice is a no-op.", + "harvested_comments": [ + "Drop any dead refs while we are here.", + "De-dupe by identity.", + "Some objects can't be weak-referenced (e.g. certain C", + "extension types). Store strongly as a fallback \u2014 the", + "caller takes responsibility for unregistering." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "unregister_remesh_hook", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3049, + "signature": "(self, op)", + "parameters": [ + { + "name": "op", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Drop an operator's ``on_remesh`` registration. Idempotent.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "value", + "name": "ephemeral_coords", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3114, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Trusted scheme-internal trial coordinate moves, restored on exit.\n\nFor schemes (e.g. RK4 surface stages) that probe trial meshes to\ncompute velocities/increments and must NOT commit a transfer. The\ncoordinates are snapshotted on enter and restored on exit, so the\nintermediate meshes are genuinely ephemeral \u2014 only the final\ncommitted move (via :meth:`deform`) updates fields + history.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "deform", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3133, + "signature": "(self, new_coords)", + "parameters": [ + { + "name": "new_coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Move the mesh to ``new_coords``, transferring all fields + history.\n\nThe public, foolproof way to impose an arbitrary node displacement\n(free surface, prescribed mesh motion). Wraps the remesh transaction\nso that REMAP variables are re-interpolated onto the new layout and\nevery registered ``on_remesh`` hook fires \u2014 in particular the\nSemiLagrangian DDt's coherent ALE (carry its history stack + a\none-step ``v_mesh = \u0394x/dt`` correction consumed by the next solve).\n\nParameters\n----------\nnew_coords : ndarray\n Target vertex coordinates (shape of ``mesh.X.coords``).\ndt : float, optional\n Timestep for the ALE ``v_mesh = \u0394x/dt`` correction. Required when\n SemiLagrangian history is present and the move is advective (a\n free-surface step); omit for a pure geometric re-mesh.\nzero : list of MeshVariable, optional\n Variables to zero after the move (e.g. V, P for a cold restart).\nverbose : bool\n\nReturns\n-------\nbool\n True if the mesh moved (geometry changed), False for a no-op.", + "harvested_comments": [ + "Refresh deformation-tracking per-boundary normals so Nitsche/penalty", + "BCs that captured ``boundary_normal(...).sym`` at setup read the new", + "geometry (the JIT reads the variable's .data, which would otherwise", + "hold the setup-time normal). Re-assemble each cached boundary normal.", + "Likewise refresh the local cell-size field (Nitsche penalty scaling)" + ], + "status": "complete", + "needs": [], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "access", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3386, + "signature": "(self, *writeable_vars)", + "parameters": [ + { + "name": "*writeable_vars", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Dummy access manager that provides deferred sync for backward compatibility.\nUses NDArray_With_Callback.delay_callbacks_global() internally.\n\nThis is a compatibility wrapper that allows existing code using the access()\ncontext manager to work with the new direct-access variable interfaces.\nAll variable modifications are deferred and synchronized at context exit.\n\nParameters\n----------\nwriteable_vars\n Variables that will be modified (ignored - all variables are writable\n with the new interface, this parameter is kept for API compatibility)\n\nReturns\n-------\nContext manager that defers variable synchronization until exit\n\nNotes\n-----\nThis method is deprecated. New code should access variable.data or\nvariable.array directly without requiring an access context.", + "harvested_comments": [ + "Use NDArray_With_Callback global delay context for deferred sync", + "This triggers all accumulated callbacks from all variables" + ], + "status": "complete", + "needs": [], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "N", "kind": "property", - "file": "src/underworld3/function/quantities.py", - "line": 128, - "signature": "(self) -> Union[float, np.ndarray]", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3436, + "signature": "(self) -> sympy.vector.CoordSys3D", "parameters": [], - "returns": "Union[float, np.ndarray]", - "existing_docstring": "Dimensional value (what the user sees).\n\nReturns\n-------\nfloat or np.ndarray\n The value in the quantity's units", + "returns": "sympy.vector.CoordSys3D", + "existing_docstring": "SymPy coordinate system for symbolic calculus.\n\nThe base coordinate system used for gradient, divergence, and\ncurl operations. Access base scalars via ``mesh.N.x``, ``mesh.N.y``,\n``mesh.N.z`` and base vectors via ``mesh.N.i``, ``mesh.N.j``, ``mesh.N.k``.\n\nReturns\n-------\nsympy.vector.CoordSys3D\n The SymPy coordinate system object.\n\nSee Also\n--------\nX : Coordinate system with data access.\nr : Tuple of coordinate scalars.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "data", + "name": "Gamma_N", "kind": "property", - "file": "src/underworld3/function/quantities.py", - "line": 140, - "signature": "(self) -> Union[float, np.ndarray]", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3456, + "signature": "(self) -> sympy.Matrix", "parameters": [], - "returns": "Union[float, np.ndarray]", - "existing_docstring": "Non-dimensional value (what the solver sees).\n\nReturns the value scaled by the model's reference quantities.\nIf no model is registered or no scaling is active, returns the\ndimensional value.\n\nReturns\n-------\nfloat or np.ndarray\n Non-dimensional value for solver use", + "returns": "sympy.Matrix", + "existing_docstring": "Normalised boundary/surface normal as a row matrix.\n\nReturns ``Gamma / |Gamma|`` so that the result is a unit normal\nregardless of element size. Use this for penalty and Nitsche BCs\nwhere mesh-independent scaling is required.\n\nReturns\n-------\nsympy.Matrix\n Row matrix of normalised boundary normal components.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "Gamma", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3472, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Raw (un-normalised) boundary coordinate scalars as a row matrix.\n\nThe magnitude scales with face edge length (2D) or face area (3D).\nFor a unit normal, use :attr:`Gamma_N` instead.\n\nReturns\n-------\nsympy.Matrix\n Row matrix of boundary coordinate scalars.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "X", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3486, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Coordinate system with symbolic coordinates and data access.\n\nThe primary interface for mesh coordinates, providing both symbolic\nexpressions for equations and numerical data for evaluation.\n\nReturns\n-------\nCoordinateSystem\n Coordinate system object with:\n\n - ``mesh.X[0]``, ``mesh.X[1]``: Symbolic coordinate functions\n - ``mesh.X.coords``: Coordinate data array (vertex positions)\n - ``mesh.X.units``: Coordinate units\n - ``x, y = mesh.X``: Unpack symbolic coordinates\n\nExamples\n--------\n>>> x, y = mesh.X # Symbolic coordinates for equations\n>>> coords = mesh.X.coords # Numerical vertex positions\n\nSee Also\n--------\nN : SymPy coordinate system for vector calculus.", "harvested_comments": [ - "Compute non-dimensional value" + "Symbolic coordinates for equations", + "Numerical vertex positions" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "magnitude", + "name": "CoordinateSystem", "kind": "property", - "file": "src/underworld3/function/quantities.py", - "line": 193, - "signature": "(self) -> Union[float, np.ndarray]", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3514, + "signature": "(self) -> CoordinateSystem", "parameters": [], - "returns": "Union[float, np.ndarray]", - "existing_docstring": "Alias for .value (Pint compatibility).", + "returns": "CoordinateSystem", + "existing_docstring": "Alias for :attr:`X` (the coordinate system object).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "units", + "name": "t", "kind": "property", - "file": "src/underworld3/function/quantities.py", - "line": 198, + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3519, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Get the Pint Unit object.\n\nReturns\n-------\npint.Unit or None\n The unit, or None if dimensionless", + "existing_docstring": "Symbolic time coordinate.\n\nPETSc passes a time value (``petsc_t``) to all pointwise residual\nand Jacobian functions. Use ``mesh.t`` in expressions to reference\nthis time without forcing JIT recompilation each timestep.\n\nThe low-level PETSc solver accepts ``time=t`` to set the value\nof ``petsc_t`` for pointwise functions. If not provided, ``petsc_t``\ndefaults to 0. Note: the high-level Python ``solve()`` wrappers\ndo not yet pass ``time=`` through \u2014 set it directly via\n``UW_DMSetTime`` at the Cython level if needed.\n\nWhen the scaling system is active, ``mesh.t`` carries time units\n(derived from the model's time scale) so that dimensional analysis\nworks correctly in expressions.\n\nExamples\n--------\n>>> omega = 2 * np.pi / period\n>>> stokes.add_dirichlet_bc((V0 * sympy.sin(omega * mesh.t), 0.0), \"Top\")\n>>> stokes.solve(time=current_time) # sets petsc_t before SNES", + "harvested_comments": [ + "sets petsc_t before SNES" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "nullspace_rotations", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3545, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Symbolic velocity fields for rigid-body rotation null modes.\n\nReturns a list of SymPy Matrix expressions in mesh Cartesian\ncoordinates. Empty for meshes with no rotation nullspace (boxes,\nwedge segments with walls). Set by mesh factory functions for\nclosed surfaces (annulus, spherical shell, etc.).\n\nEach entry represents a rigid rotation: v = omega x r.\n\nReturns\n-------\nlist of sympy.Matrix\n Velocity fields for each independent rotation mode.\n\nExamples\n--------\n>>> annulus = uw.meshing.Annulus(...)\n>>> annulus.nullspace_rotations # [Matrix([-y, x])]\n>>> shell = uw.meshing.SphericalShell(...)\n>>> shell.nullspace_rotations # 3 rotation matrices", + "harvested_comments": [ + "[Matrix([-y, x])]", + "3 rotation matrices" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "r", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3570, + "signature": "(self) -> Tuple[sympy.vector.BaseScalar]", + "parameters": [], + "returns": "Tuple[sympy.vector.BaseScalar]", + "existing_docstring": "Tuple of coordinate scalars :math:`(x, y)` or :math:`(x, y, z)`.\n\nReturns\n-------\ntuple\n Tuple of SymPy base scalars ``(N.x, N.y[, N.z])``.\n\nSee Also\n--------\nrvec : Position vector form.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "has_units", + "name": "rvec", "kind": "property", - "file": "src/underworld3/function/quantities.py", - "line": 210, - "signature": "(self) -> bool", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3585, + "signature": "(self) -> sympy.vector.Vector", "parameters": [], - "returns": "bool", - "existing_docstring": "Check if this quantity has units.", + "returns": "sympy.vector.Vector", + "existing_docstring": "Position vector :math:`\\mathbf{r} = x\\hat{i} + y\\hat{j} [+ z\\hat{k}]`.\n\nReturns\n-------\nsympy.vector.Vector\n The position vector in the mesh coordinate system.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "dimensionality", + "name": "data", "kind": "property", - "file": "src/underworld3/function/quantities.py", - "line": 215, - "signature": "(self) -> dict", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3605, + "signature": "(self) -> numpy.ndarray", "parameters": [], - "returns": "dict", - "existing_docstring": "Get the Pint dimensionality dictionary.\n\nReturns\n-------\ndict\n e.g., {'[length]': 1, '[time]': -1} for velocity", + "returns": "numpy.ndarray", + "existing_docstring": "The array of mesh element vertex coordinates.\n\n.. deprecated:: 0.99.0\n Use :attr:`X.coords` instead.\n ``mesh.data`` is deprecated in favor of ``mesh.X.coords``\n (coordinate-system-aware interface).\n\nThis is an alias for mesh.points (which is also deprecated).", "harvested_comments": [], "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "points", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3624, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mesh node coordinates in physical units.\n\n.. deprecated:: 0.99.0\n Use :attr:`X.coords` instead.\n ``mesh.points`` is deprecated in favor of ``mesh.X.coords``\n (coordinate-system-aware interface).\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from internal model coordinates\nto physical coordinates for user access.\n\nWhen the mesh has coordinate units specified, returns a unit-aware array.\n\nReturns:\n numpy.ndarray or UnitAwareArray: Node coordinates (with units if specified)", + "harvested_comments": [ + "Apply scaling to convert model coordinates to physical coordinates", + "Wrap with unit-aware array if units are specified" + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "to", + "name": "points", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 232, - "signature": "(self, target_units: str) -> 'UWQuantity'", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3666, + "signature": "(self, value)", "parameters": [ { - "name": "target_units", - "type_hint": "str", + "name": "value", + "type_hint": null, "default": null, "description": "" } ], - "returns": "'UWQuantity'", - "existing_docstring": "Convert to different units.\n\nParameters\n----------\ntarget_units : str\n Target units (e.g., \"m/s\", \"km\", \"degC\")\n\nReturns\n-------\nUWQuantity\n New quantity with converted value and units", + "returns": null, + "existing_docstring": "Removed. Move mesh nodes with :meth:`deform`.\n\nThe deprecated setter rebound ``self._coords`` to a plain ndarray,\nsilently discarding the ``NDArray_With_Callback`` wrapper: the\ndeform callback never fired, so PETSc coordinates, kd-trees and\ndependent caches were never updated \u2014 writes looked accepted but\nchanged nothing downstream (2026-07 audit, BF-18 / READ-43).\nRather than repair an already-deprecated write path, it now\nrefuses loudly.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "UWQuantity", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "to_base_units", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 252, - "signature": "(self) -> 'UWQuantity'", + "name": "physical_coordinates", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3685, + "signature": "(self)", "parameters": [], - "returns": "'UWQuantity'", - "existing_docstring": "Convert to SI base units.", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": "Mesh coordinates in physical units.\n\nReturns the mesh coordinate array scaled to physical units using\nthe model's length scale. Requires the mesh to be associated with\na model that has reference quantities set.\n\nReturns\n-------\nUWQuantity or None\n Coordinates in physical units, or None if no model scaling available\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_length=1000*uw.units.km, ...)\n>>> mesh = uw.meshing.StructuredQuadBox(...)\n>>> physical_coords = mesh.physical_coordinates # In kilometers", + "harvested_comments": [ + "In kilometers" + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "to_reduced_units", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 260, - "signature": "(self) -> 'UWQuantity'", + "name": "physical_bounds", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3710, + "signature": "(self)", "parameters": [], - "returns": "'UWQuantity'", - "existing_docstring": "Simplify units by canceling common factors.", + "returns": null, + "existing_docstring": "Mesh bounds in physical units.\n\nReturns the mesh bounding box scaled to physical units using\nthe model's length scale.\n\nReturns\n-------\ntuple of UWQuantity or None\n (min_coords, max_coords) in physical units, or None if no model scaling\n\nExamples\n--------\n>>> physical_min, physical_max = mesh.physical_bounds\n>>> print(f\"Domain: {physical_min} to {physical_max}\")", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "to_compact", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 268, - "signature": "(self) -> 'UWQuantity'", + "name": "physical_extent", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3741, + "signature": "(self)", "parameters": [], - "returns": "'UWQuantity'", - "existing_docstring": "Convert to most readable unit representation.", + "returns": null, + "existing_docstring": "Mesh spatial extent in physical units.\n\nReturns the mesh size (max - min) in each dimension scaled to physical units.\n\nReturns\n-------\nUWQuantity or None\n Extent in physical units, or None if no model scaling\n\nExamples\n--------\n>>> extent = mesh.physical_extent\n>>> print(f\"Domain size: {extent}\")", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "diff", + "name": "write_timestep", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 751, - "signature": "(self, *args, **kwargs)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3769, + "signature": "(self, filename: str, index: int, outputPath: Optional[str] = '', meshVars: Optional[list] = [], swarmVars: Optional[list] = [], meshUpdates: bool = False, create_xdmf: bool = True, petsc_reload: bool = False)", "parameters": [ { - "name": "*args", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" }, { - "name": "**kwargs", - "type_hint": null, + "name": "index", + "type_hint": "int", "default": null, "description": "" + }, + { + "name": "outputPath", + "type_hint": "Optional[str]", + "default": "''", + "description": "" + }, + { + "name": "meshVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "swarmVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "meshUpdates", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "create_xdmf", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "petsc_reload", + "type_hint": "bool", + "default": "False", + "description": "" } ], "returns": null, - "existing_docstring": "Derivative of a constant is zero.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Write mesh and selected variables for timestep output.\n\nThis is the standard mesh output method. It always writes:\n\n- one mesh HDF5 file, shared across timesteps unless ``meshUpdates=True``\n- one HDF5 file per mesh variable\n- raw coordinate/value datasets under ``/fields`` for coordinate-based\n reload with ``MeshVariable.read_timestep()``\n\nThe optional payloads are controlled explicitly:\n\n- ``create_xdmf=True`` writes ParaView/XDMF output. Variable files also\n receive ``/vertex_fields`` or ``/cell_fields`` compatibility groups,\n and rank 0 writes the companion ``.xdmf`` file.\n- ``petsc_reload=True`` writes PETSc DMPlex section/vector metadata into\n the same per-variable HDF5 files. These files can then be loaded with\n ``MeshVariable.read_checkpoint()`` for PETSc-native same-mesh reload.\n\nCommon choices are:\n\n- visualisation/remap only:\n ``create_xdmf=True, petsc_reload=False``\n- PETSc-native reload only:\n ``create_xdmf=False, petsc_reload=True``\n- unified visualisation/remap and PETSc reload:\n ``create_xdmf=True, petsc_reload=True``\n\nWith both flags enabled, the same variable HDF5 file can be used by\n``MeshVariable.read_timestep()`` for coordinate/KDTree remapping and by\n``MeshVariable.read_checkpoint()`` for exact PETSc-native reload.\n\nParameters\n----------\nfilename\n Output filename base. Files are written as\n ``.mesh..h5`` and\n ``.mesh...h5``.\nindex\n Timestep/output index used in generated filenames.\noutputPath\n Directory where output files are written.\nmeshVars\n Mesh variables to write.\nswarmVars\n Swarm variables to write as proxy fields.\nmeshUpdates\n If ``False``, reuse ``.mesh.00000.h5`` when it already\n exists. If ``True``, write an indexed mesh file for this timestep.\ncreate_xdmf\n Write ParaView/XDMF-compatible datasets and companion XDMF file.\npetsc_reload\n Write PETSc DMPlex section/vector metadata for reload with\n ``MeshVariable.read_checkpoint()``.", + "harvested_comments": [ + "check the directory where we will write checkpoint", + "get directory", + "check if path exists", + "easier to debug abs", + "check if we have write access" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "UWQuantity", + "parent_class": "Mesh", "is_public": true }, { - "name": "quantity", - "kind": "function", - "file": "src/underworld3/function/quantities.py", - "line": 834, - "signature": "(value: Union[float, int, np.ndarray], units: Optional[str] = None) -> UWQuantity", + "name": "petsc_save_checkpoint", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3903, + "signature": "(self, index: int, meshVars: Optional[list] = [], outputPath: Optional[str] = '')", "parameters": [ { - "name": "value", - "type_hint": "Union[float, int, np.ndarray]", + "name": "index", + "type_hint": "int", "default": null, "description": "" }, { - "name": "units", + "name": "meshVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "outputPath", "type_hint": "Optional[str]", - "default": "None", + "default": "''", "description": "" } ], - "returns": "UWQuantity", - "existing_docstring": "Create a unit-aware quantity.\n\nParameters\n----------\nvalue : float, int, or array-like\n The numerical value\nunits : str, optional\n Units specification (e.g., \"Pa*s\", \"cm/year\", \"K\")\n\nReturns\n-------\nUWQuantity\n Unit-aware quantity\n\nExamples\n--------\n>>> viscosity = uw.quantity(1e21, \"Pa*s\")\n>>> velocity = uw.quantity(5, \"cm/year\")\n>>> dT = uw.quantity(1000, \"K\") - uw.quantity(273, \"K\")", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "returns": null, + "existing_docstring": "Save the mesh and mesh variables to HDF5 with XDMF.\n\nThis is a convenience wrapper around ``write_timestep()`` that\nprovides the simpler interface used by earlier Underworld3 code.\nOutput uses the same per-variable file layout and XDMF generation\n(including vertex/cell compatibility groups, field projection, and\ntensor repacking) as ``write_timestep()``.\n\nParameters\n----------\nmeshVars :\n List of UW mesh variables to save. If left empty then just\n the mesh is saved.\nindex :\n An index which might correspond to the timestep or output\n number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data\n in the current working directory.", + "harvested_comments": [ + "Split outputPath into directory and filename base for write_timestep().", + "Old callers pass outputPath like './output/' or './output/run_name'.", + "Directory only \u2014 use 'checkpoint' as the file base name", + "Path with filename component", + "Bare name, no directory" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "has_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 95, - "signature": "(obj)", + "name": "write_checkpoint", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4004, + "signature": "(self, filename: str, outputPath: str = '', meshUpdates: bool = True, meshVars: Optional[list] = [], swarmVars: Optional[list] = [], index: Optional[int] = 0, unique_id: Optional[bool] = False, separate_variable_files: bool = True, create_xdmf: bool = False)", "parameters": [ { - "name": "obj", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" + }, + { + "name": "outputPath", + "type_hint": "str", + "default": "''", + "description": "" + }, + { + "name": "meshUpdates", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "meshVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "swarmVars", + "type_hint": "Optional[list]", + "default": "[]", + "description": "" + }, + { + "name": "index", + "type_hint": "Optional[int]", + "default": "0", + "description": "" + }, + { + "name": "unique_id", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "separate_variable_files", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "create_xdmf", + "type_hint": "bool", + "default": "False", + "description": "" } ], "returns": null, - "existing_docstring": "Check if an object has unit information.\n\nParameters\n----------\nobj : any\n Object to check for unit information\n\nReturns\n-------\nbool\n True if object has detectable units", + "existing_docstring": "Compatibility wrapper for PETSc DMPlex reload output.\n\nThis method is retained for existing callers. New code should use\n``write_timestep(..., petsc_reload=True)`` so all mesh-variable output\ngoes through the standard timestep writer. By default this compatibility\nmethod writes PETSc DMPlex section/vector metadata required for exact\nparallel reload and does not write XDMF or vertex-field visualisation\ndatasets. Use ``create_xdmf=True`` to route through the unified\ntimestep-style output path.\n\nParameters\n----------\nfilename\n Checkpoint base filename. With ``outputPath`` unset, this may include\n a directory. With ``outputPath`` set, it is joined to that directory.\noutputPath\n Optional output directory, matching the ``write_timestep()`` style.\nmeshUpdates\n If ``False``, write the mesh checkpoint only when it does not already\n exist. If ``True``, always write the indexed mesh checkpoint.\nmeshVars, swarmVars\n Variables to write into checkpoint files.\nindex\n Checkpoint index used in output filenames.\nunique_id\n Preserve existing unique-rank filename behaviour for checkpoint data.\nseparate_variable_files\n If ``True`` (default), write one file per variable:\n ``...h5``. If ``False``, write all variables\n into one file: ``.checkpoint..h5``.\ncreate_xdmf\n If ``True``, route through ``write_timestep()`` and write XDMF,\n vertex/cell compatibility groups, coordinate/KDTree remap data,\n and PETSc reload metadata. The output uses the timestep filename\n convention ``.mesh...h5``. This mode does\n not support ``unique_id=True`` or ``separate_variable_files=False``.", "harvested_comments": [ - "Check for UWQuantity", - "Check for Pint quantity", - "Check for array with unit metadata", - "Check for NDArray with unit information" + "The mesh checkpoint is the same as the one required for visualisation" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "compute_expression_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 133, - "signature": "(expr)", - "parameters": [ - { - "name": "expr", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Compute units for compound SymPy expressions using dimensional analysis.\n\nThis function traverses the expression tree and uses Pint to perform\ndimensional arithmetic on the units of sub-expressions.\n\nParameters\n----------\nexpr : sympy expression\n Expression to analyze (e.g., temperature / y)\n\nReturns\n-------\npint.Unit or None\n Computed unit object, or None if cannot determine\n\nExamples\n--------\n>>> # T.sym has units 'kelvin', y has units 'kilometer'\n>>> compute_expression_units(T.sym / y)\n\n\n>>> # velocity has units 'm/s', time has units 's'\n>>> compute_expression_units(velocity * time)\n\n\n>>> # Derivative: dT/dx\n>>> compute_expression_units(T.sym.diff(mesh.N.x))\n\n\nChanged in 2025-10-16: Now returns pint.Unit objects instead of strings.", + "name": "snapshot_payload", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4144, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Return a self-contained dict describing this mesh's state.\n\nThe returned dict is consumed by\n:mod:`underworld3.checkpoint.snapshot` capture. Keys:\n\n- ``name``: stable string identifier for the mesh.\n- ``mesh_version``: current ``_mesh_version`` integer.\n- ``coords``: deformed mesh coordinates (numpy array).\n- ``vars``: ``{var.clean_name: gvec_array.copy()}`` for every\n mesh variable on this mesh.\n\nv1.2 will additionally populate a ``topology`` key with\nsection / DM-topology data sufficient to rebuild the DM on\nrestore.", "harvested_comments": [ - "T.sym has units 'kelvin', y has units 'kilometer'", - "velocity has units 'm/s', time has units 's'", - "Derivative: dT/dx", - "Helper to check if a pint.Unit is dimensionless", - "Priority -1: Check for DERIVATIVES first (before general UnderworldFunction)" + "Variables created but never touched have _gvec=None (lazy", + "allocation in MeshVariable). They carry no data so they", + "contribute nothing to the snapshot \u2014 skip cleanly." ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "get_mesh_coordinate_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 379, - "signature": "(mesh_or_expr)", + "name": "apply_snapshot_payload", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4177, + "signature": "(self, payload: dict) -> None", "parameters": [ { - "name": "mesh_or_expr", - "type_hint": null, + "name": "payload", + "type_hint": "dict", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Get the coordinate units expected by a mesh or expression.\n\nParameters\n----------\nmesh_or_expr : Mesh or sympy expression\n Mesh object or expression containing mesh variables\n\nReturns\n-------\ndict or None\n Dictionary with coordinate unit information, or None if not available", + "returns": "None", + "existing_docstring": "Restore this mesh from a payload produced by :meth:`snapshot_payload`.\n\nv1 implementation writes coordinates and per-variable DOFs\nback in place. The captured DOF arrays must match the current\nsection, which means ``_mesh_version`` must equal the captured\nvalue \u2014 mesh-adapt during the interval would have resized the\nsection and is detected as a v1 refusal here.\n\nv1.2 will replace the ``_mesh_version`` refusal with a\nrebuild-from-payload path: destroy the current DM, rebuild\nfrom ``payload[\"topology\"]``, allocate vectors, write DOFs,\nand re-bind MeshVariable / Swarm wrappers. The interface stays\nthe same; only this method's body changes.", "harvested_comments": [ - "Try to extract mesh from expression", - "Get coordinate system information", - "Check if mesh has coordinate scaling (units applied)", - "Return unit information - internal units are what the mesh expects", - "Mesh expects internal model units" + "Snapshot restore: variables are reloaded just below, so this is a", + "sanctioned internal coordinate move (no live-state transfer needed)." ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "convert_coordinates_to_mesh_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 421, - "signature": "(coords, mesh_info, coord_units = None)", + "name": "write", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4241, + "signature": "(self, filename: str, index: Optional[int] = None, petsc_format: Optional[bool] = None)", "parameters": [ { - "name": "coords", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" }, { - "name": "mesh_info", - "type_hint": null, - "default": null, + "name": "index", + "type_hint": "Optional[int]", + "default": "None", "description": "" }, { - "name": "coord_units", - "type_hint": null, + "name": "petsc_format", + "type_hint": "Optional[bool]", "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Convert coordinate array to mesh unit system with explicit unit specification.\n\nFollowing UW3 policy: no implicit unit conversions. Coordinates must have\nexplicit units or are assumed to be in model units.\n\nParameters\n----------\ncoords : array-like\n Coordinate array\nmesh_info : dict\n Mesh coordinate unit information from get_mesh_coordinate_units()\ncoord_units : str, optional\n Explicit coordinate units. If None, assumes model coordinates.\n\nReturns\n-------\nnumpy.ndarray\n Coordinates converted to mesh unit system\n\nRaises\n------\nValueError\n If coordinate units are specified but mesh has no scaling context", + "existing_docstring": "Save mesh data to the specified hdf5 file.\n\n\nParameters\n----------\nfilename :\n The filename for the mesh checkpoint file.\nindex :\n Not yet implemented. An optional index which might\n correspond to the timestep (for example).\npetsc_format :\n If True, force PETSc DMPlex HDF5 checkpoint/restart topology.\n If False, force PETSc HDF5_VIZ topology only.\n If None, use PETSc's default HDF5 layout, which includes the\n restart-style topology and labels as well as visualization\n topology for XDMF.", "harvested_comments": [ - "Extract coordinate values and ensure float64", - "If no mesh info or mesh is not scaled, coordinates must be model units", - "For scaled meshes with explicit coordinate units", - "Convert physical coordinates to model coordinates", - "Create a temporary quantity for proper unit conversion" + "# JM:To enable timestep recording, the following needs to be called.", + "# I'm unsure if the corresponding xdmf functionality is enabled via", + "# the PETSc xdmf script.", + "viewer.pushTimestepping(viewer)", + "viewer.setTimestep(index)" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "detect_coordinate_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 494, - "signature": "(coords)", + "name": "vtk", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4371, + "signature": "(self, filename: str)", "parameters": [ { - "name": "coords", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Detect what unit system coordinates are in.\n\nParameters\n----------\ncoords : array-like\n Coordinate array\n\nReturns\n-------\ndict\n Information about coordinate units", + "existing_docstring": "Save mesh to the specified file", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "add_expression_units_to_result", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 515, - "signature": "(result, expression, mesh_info)", + "name": "generate_xdmf", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4380, + "signature": "(self, filename: str)", "parameters": [ { - "name": "result", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "This method generates an xdmf schema for the specified file.\n\nThe filename of the generated file will be the same as the hdf5 file\nbut with the `xmf` extension.\n\nParameters\n----------\nfilename :\n File name of the checkpointed hdf5 file for which the\n xdmf schema will be written.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "vars", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4402, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "A list of variables recorded on the mesh.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "block_vars", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4411, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "A list of variables recorded on the mesh.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "points_in_domain", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5078, + "signature": "(self, points, strict_validation = True)", + "parameters": [ { - "name": "expression", + "name": "points", "type_hint": null, "default": null, "description": "" }, { - "name": "mesh_info", + "name": "strict_validation", "type_hint": null, - "default": null, + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": "Add appropriate units to evaluation result based on expression analysis.\n\nAnalyzes the expression to determine its physical units and converts\nthe model-unit result back to appropriate physical units.\n\nParameters\n----------\nresult : numpy.ndarray\n Raw evaluation result in model units from PETSc\nexpression : sympy expression\n Expression that was evaluated\nmesh_info : dict\n Mesh coordinate unit information\n\nReturns\n-------\narray or UWQuantity\n Result with appropriate units if detectable, otherwise plain array", + "existing_docstring": "Determine if the given points lie in this domain.\nUses a mesh-boundary skeletonization array to determine whether the point is\ninside the boundary or outside. If close to the boundary, it checks if points\nare in a cell.\n\nParameters\n----------\npoints : array-like\n Coordinate array in any physical unit system (will be auto-converted).\n Plain numbers are assumed to be in model coordinates.\nstrict_validation : bool\n Whether to perform strict validation near boundaries", "harvested_comments": [ - "Try to determine the units of the expression", - "Convert result from model units to physical units", - "No units detectable - return plain array (likely dimensionless)", - "If unit analysis fails, return plain result" + "Convert points to model coordinates using the unified conversion function", + "This handles all coordinate formats: plain numbers, unit-aware coordinates, lists, tuples, arrays", + "_convert_coords_to_si now converts to model coordinates (despite the name)", + "and handles all the complexity of extracting values from unit-aware coordinates", + "Cd-1 surface mesh: no boundary-face control points exist" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "determine_expression_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 554, - "signature": "(expression, mesh_info)", + "name": "get_closest_cells", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5165, + "signature": "(self, coords: numpy.ndarray) -> numpy.ndarray", "parameters": [ { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "mesh_info", - "type_hint": null, + "name": "coords", + "type_hint": "numpy.ndarray", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Determine the physical units of a SymPy expression.\n\nAnalyzes the expression to infer what units the result should have\nbased on the constituent variables and operations. This now uses\ndimensional arithmetic for compound expressions.\n\nParameters\n----------\nexpression : sympy expression\n Expression to analyze\nmesh_info : dict\n Mesh coordinate unit information (optional, can be None)\n\nReturns\n-------\nstr or None\n Unit string if determinable, None if dimensionless or unknown\n\nNotes\n-----\nThis function now delegates to compute_expression_units() which performs\ndimensional arithmetic using Pint. This ensures consistent behavior between\nget_units() and determine_expression_units().", + "returns": "numpy.ndarray", + "existing_docstring": "This method uses a kd-tree algorithm to find the closest\ncells to the provided coords. For a regular mesh, this should\nbe exactly the owning cell, but if the mesh is deformed, this\nis not guaranteed. Note, the nearest point may not be all\nthat close by - use get_closest_local_cells to filter out points\nthat are (probably) not within any local cell.\n\nParameters:\n-----------\ncoords:\n An array of the coordinates for which we wish to determine the\n closest cells. This should be a 2-dimensional array of\n shape (n_coords,dim) in any physical unit system (will be auto-converted).\n Plain numbers are assumed to be in model coordinates.\n\nReturns:\n--------\nclosest_cells:\n An array of indices representing the cells closest to the provided\n coordinates. This will be a 1-dimensional array of\n shape (n_coords).", "harvested_comments": [ - "Use the unified dimensional analysis function", - "If analysis fails, assume dimensionless" + "Convert coords to model coordinates", + "Simply extract raw values - np.asarray handles unit-aware objects correctly", + "## returns an empty 1D array if no coords are provided", + "CRITICAL: Must return 1D array, not 2D, for Cython buffer compatibility" ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "add_units_to_result", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 588, - "signature": "(result, expression)", + "name": "test_if_points_in_cells", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5326, + "signature": "(self, points, cells, on_boundary = True, tol = 0.0)", "parameters": [ { - "name": "result", + "name": "points", "type_hint": null, "default": null, "description": "" }, { - "name": "expression", + "name": "cells", "type_hint": null, "default": null, "description": "" + }, + { + "name": "on_boundary", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "tol", + "type_hint": null, + "default": "0.0", + "description": "" } ], "returns": null, - "existing_docstring": "Add appropriate units to evaluation result based on expression.\n\nParameters\n----------\nresult : numpy.ndarray\n Raw evaluation result\nexpression : sympy expression\n Expression that was evaluated\n\nReturns\n-------\narray or UWQuantity\n Result with appropriate units if detectable", + "existing_docstring": "Determine if the given points lie in the suggested cells.\nUses a mesh skeletonization array to determine whether the point is\nwith the convex polygon / polyhedron defined by a cell.\n\nExact if applied to a linear mesh, approximate otherwise.\n\nParameters\n----------\npoints : array-like\n Coordinate array in any physical unit system (will be auto-converted)\ncells : array-like\n Cell indices to test\non_boundary : bool, default True\n If True (the default), points exactly on a cell face count as\n inside the cell (natural for FE evaluation, where the basis at\n a shared face/vertex is consistent across adjacent cells). If\n False, points on the closure of a cell are reported as NOT in\n it (strict-inside semantics \u2014 useful when uniqueness matters).\ntol : float, default 0.0\n Face-relative tolerance forwarded to\n `_test_if_points_in_cells_internal`. When ``> 0`` takes\n precedence over ``on_boundary``: the test admits points\n within ``tol`` of the face relative to the control-point\n separation\u00b2 \u2014 used by the parallel evaluation locator\n for on-face / near-face queries at the mesh-spacing scale.\n\nReturns\n-------\nnumpy.ndarray\n Boolean array indicating if points are in cells", "harvested_comments": [ - "This is the old function - kept for backward compatibility", - "New function is add_expression_units_to_result" + "Convert points to model units using the elegant protocol", + "Extract numerical values for internal mesh operations", + "Coerce cells to a 1-D numpy array \u2014 accept list/tuple input as the", + "docstring promises (\"array-like\") even though the internal helper", + "calls cells.reshape(-1) directly." ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "convert_quantity_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 609, - "signature": "(quantity, target_units)", + "name": "get_closest_local_cells", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5381, + "signature": "(self, coords: numpy.ndarray, on_boundary: bool = True, tol: float = 0.0) -> numpy.ndarray", "parameters": [ { - "name": "quantity", - "type_hint": null, + "name": "coords", + "type_hint": "numpy.ndarray", "default": null, "description": "" }, { - "name": "target_units", - "type_hint": null, - "default": null, + "name": "on_boundary", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "tol", + "type_hint": "float", + "default": "0.0", "description": "" } ], - "returns": null, - "existing_docstring": "Convert UWQuantity or Pint quantity to target units.\n\nParameters\n----------\nquantity : UWQuantity, Pint quantity, or array-like\n The quantity to convert\ntarget_units : str or Pint unit\n Target units to convert to\n\nReturns\n-------\nconverted quantity\n Quantity converted to target units", + "returns": "numpy.ndarray", + "existing_docstring": "This method uses a kd-tree algorithm to find the closest\ncells to the provided coords. For a regular mesh, this should\nbe exactly the owning cell, but if the mesh is deformed, this\nis not guaranteed. Also compares the distance from the cell to the\npoint - if this is larger than the \"cell size\" then returns -1\n\nParameters:\n-----------\ncoords:\n An array of the coordinates for which we wish to determine the\n closest cells. This should be a 2-dimensional array of\n shape (n_coords,dim) in any physical unit system (will be auto-converted).\non_boundary : bool, default True\n If True (the default), queries exactly on a cell face are\n treated as inside that cell (natural for FE-evaluation hints \u2014\n mesh vertices sit on cell faces by definition). If False,\n strict-inside semantics; boundary queries return -1.\ntol : float, default 0.0\n Face-relative tolerance forwarded to\n `_test_if_points_in_cells_internal`. When ``> 0`` takes\n precedence over ``on_boundary``: the test admits points\n within ``tol`` of the face relative to the control-point\n separation\u00b2 \u2014 used by the parallel evaluation locator\n for on-face / near-face queries at the mesh-spacing scale.\n\nReturns:\n--------\nclosest_cells:\n An array of indices representing the cells closest to the provided\n coordinates. This will be a 1-dimensional array of\n shape (n_coords).", "harvested_comments": [ - "Handle UWQuantity", - "Convert using Pint", - "Return new UWQuantity", - "Handle direct Pint quantity", - "Handle plain arrays - assume they're already in target units" + "Convert coords to model units using the elegant protocol", + "Extract numerical values for internal mesh operations", + "Call internal implementation" ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "detect_quantity_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 643, - "signature": "(obj)", - "parameters": [ - { - "name": "obj", - "type_hint": null, - "default": null, - "description": "" - } + "name": "get_min_radius_old", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5575, + "signature": "(self) -> float", + "parameters": [], + "returns": "float", + "existing_docstring": "This method returns the global minimum distance from any cell centroid to a face.\nIt wraps to the PETSc `DMPlexGetMinRadius` routine. The petsc4py equivalent always\nreturns zero.", + "harvested_comments": [ + "# Note: The petsc4py version of DMPlexComputeGeometryFVM does not compute all cells and", + "# does not obtain the minimum radius for the mesh." ], - "returns": null, - "existing_docstring": "Detect units of any object (UWQuantity, Pint, array with metadata).\n\nParameters\n----------\nobj : any\n Object to detect units from\n\nReturns\n-------\ndict\n Dictionary with unit information:\n - 'has_units': bool\n - 'units': str or None\n - 'is_dimensionless': bool\n - 'unit_type': str ('UWQuantity', 'Pint', 'metadata', 'none')", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "get_min_radius", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5593, + "signature": "(self) -> float", + "parameters": [], + "returns": "float", + "existing_docstring": "This method returns the global minimum distance from any cell centroid to a face.\nIt wraps to the PETSc `DMPlexGetMinRadius` routine. The petsc4py equivalent always\nreturns zero.", "harvested_comments": [ - "Check for UWQuantity", - "Check for Pint quantity", - "Check for array with unit metadata", - "Check for NDArray with unit information", - "No units detected" + "# Note: The petsc4py version of DMPlexComputeGeometryFVM does not compute all cells and", + "# does not obtain the minimum radius for the mesh." ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "make_dimensionless", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 706, - "signature": "(quantity, reference_scales)", + "name": "get_max_radius", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5610, + "signature": "(self) -> float", + "parameters": [], + "returns": "float", + "existing_docstring": "This method returns the global maximum distance from any cell centroid to a face.", + "harvested_comments": [ + "# Note: The petsc4py version of DMPlexComputeGeometryFVM does not compute all cells and", + "# does not obtain the minimum radius for the mesh." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "get_mean_radius", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5625, + "signature": "(self) -> float", + "parameters": [], + "returns": "float", + "existing_docstring": "Global mean of the characteristic cell length scale\n(``volume^(1/dim)``, i.e. the equivalent radius derived from each\ncell's volume \u2014 the same quantity averaged by ``get_min_radius``\nand ``get_max_radius`` to obtain global min/max). Parallel-safe\nvia MPI allreduce of the local sum and count.\n\nTogether with :meth:`get_min_radius` / :meth:`get_max_radius`\nthis is the canonical \"mesh length\" API. Use this anywhere you\nneed a representative h0 (smoothing-length defaults, diffusion-\nstability heuristics, problem-scale normalisation) rather than\nreaching for the rank-local ``self._radii`` array, which gives\ndifferent answers on different MPI ranks and leaks downstream\n(e.g. into JIT C source via per-rank pointwise-function inputs).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Mesh", + "is_public": true + }, + { + "name": "stats", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5654, + "signature": "(self, uw_function, uw_meshVariable, basis = None)", "parameters": [ { - "name": "quantity", + "name": "uw_function", "type_hint": null, "default": null, "description": "" }, { - "name": "reference_scales", + "name": "uw_meshVariable", "type_hint": null, "default": null, "description": "" + }, + { + "name": "basis", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Convert physical quantity to dimensionless using reference scales.\n\nParameters\n----------\nquantity : UWQuantity or Pint quantity\n Physical quantity to make dimensionless\nreference_scales : dict or Model\n Dictionary of reference scales or Model with reference quantities\n\nReturns\n-------\nUWQuantity\n Dimensionless quantity", + "existing_docstring": "Returns various norms on the mesh for the provided function.\n - size\n - mean\n - min\n - max\n - sum\n - L2 norm\n - rms\n\n NOTE: this currently assumes scalar variables !", "harvested_comments": [ - "Handle Model object", - "Get quantity info", - "Already dimensionless", - "Extract Pint quantity", - "Determine appropriate scale based on quantity dimensions" + "This uses a private work MeshVariable and the various norms defined there but", + "could either be simplified to just use petsc vectors, or extended to", + "compute integrals over the elements which is in line with uw1 and uw2" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "convert_array_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 795, - "signature": "(array, from_units, to_units)", + "name": "meshVariable_mask_from_label", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5690, + "signature": "(self, label_name, label_value)", "parameters": [ { - "name": "array", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "from_units", + "name": "label_name", "type_hint": null, "default": null, "description": "" }, { - "name": "to_units", + "name": "label_value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array from one unit system to another.\n\nParameters\n----------\narray : array-like\n Array values to convert\nfrom_units : str or Pint unit\n Source units\nto_units : str or Pint unit\n Target units\n\nReturns\n-------\nnumpy.ndarray\n Converted array values", - "harvested_comments": [ - "Create a temporary quantity for conversion", - "Extract the magnitude" + "existing_docstring": "Extract single label value and make a point mask - note: this produces a mask on the mesh points and\nassumes a 1st order mesh. Cell labels are not respected in this function.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "auto_convert_to_mesh_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 828, - "signature": "(array, mesh)", + "name": "register_swarm", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5716, + "signature": "(self, swarm)", "parameters": [ { - "name": "array", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "mesh", + "name": "swarm", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array coordinates to mesh unit system.\n\nParameters\n----------\narray : array-like\n Coordinate array that may have units\nmesh : Mesh\n Mesh to get unit system from\n\nReturns\n-------\nnumpy.ndarray\n Coordinates converted to mesh unit system", + "existing_docstring": "Register swarm as dependent on this mesh for coordinate change notifications", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "convert_evaluation_result", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 848, - "signature": "(result, target_units)", + "name": "unregister_swarm", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5720, + "signature": "(self, swarm)", "parameters": [ { - "name": "result", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "target_units", + "name": "swarm", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert evaluation results to target unit system.\n\nParameters\n----------\nresult : array-like or UWQuantity\n Evaluation result to convert\ntarget_units : str or Pint unit\n Target units to convert to\n\nReturns\n-------\nconverted result\n Result converted to target units", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "existing_docstring": "Unregister swarm (called during swarm cleanup)", + "harvested_comments": [ + "WeakSet handles weak references internally, just remove the swarm directly" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "add_units", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 867, - "signature": "(array, units_str)", + "name": "register_surface", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5725, + "signature": "(self, surface)", "parameters": [ { - "name": "array", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "units_str", + "name": "surface", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Add unit metadata to plain array.\n\nParameters\n----------\narray : array-like\n Plain array to add units to\nunits_str : str\n Unit string to associate with array\n\nReturns\n-------\nUWQuantity\n Array wrapped with unit information", + "existing_docstring": "Register surface as dependent on this mesh for adaptation notifications.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", "is_public": true }, { - "name": "make_evaluate_unit_aware", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 888, - "signature": "(original_evaluate_func)", + "name": "unregister_surface", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5729, + "signature": "(self, surface)", "parameters": [ { - "name": "original_evaluate_func", + "name": "surface", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Decorator to make evaluate functions unit-aware with explicit unit specification.\n\nThis wraps the original evaluate function to:\n1. Accept explicit coordinate units parameter\n2. Convert coordinates to mesh units if units are specified\n3. Assume model coordinates if no units specified\n4. Convert results back to appropriate physical units\n\nFollowing UW3 policy: no implicit unit detection or conversion.\n\nParameters\n----------\noriginal_evaluate_func : callable\n Original evaluate function to wrap\n\nReturns\n-------\ncallable\n Unit-aware version of the function", - "harvested_comments": [ - "Auto-extract .sym from MeshVariable for user convenience", - "This is likely a MeshVariable - extract the symbolic representation", - "Handle the case where no coordinates are provided", - "Get mesh coordinate unit information", - "Approach 1: Use coord_sys if provided" + "existing_docstring": "Unregister surface (called during surface cleanup).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "Mesh", "is_public": true }, { - "name": "query", + "name": "OT_adapt", "kind": "method", - "file": "src/underworld3/kdtree.py", - "line": 149, - "signature": "(self, coords, k = 1, sqr_dists = True)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5744, + "signature": "(self, field)", "parameters": [ { - "name": "coords", + "name": "field", "type_hint": null, "default": null, "description": "" - }, - { - "name": "k", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "sqr_dists", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, - "existing_docstring": "Find the n points closest to the provided coordinates.\n\nThis method is unit-aware: if the KD-tree was built with unit-aware coordinates,\nit will automatically convert query coordinates to match and return distances\nwith appropriate units.\n\nParameters\n----------\ncoords : array-like\n An array of coordinates for which the kd-tree index will be searched for nearest\n neighbours. This should be a 2-dimensional array of size (n_coords, dim).\n Can be unit-aware (UnitAwareArray) or plain numpy array.\n If KD-tree has coordinate units, coords must have compatible units.\nk : int, optional\n The number of nearest neighbour points to find for each `coords` (default 1).\nsqr_dists : bool, optional\n Set to True to return the squared distances, set to False to return the actual\n distances (default True).\n\nReturns\n-------\nd : array\n A float array of the squared (sqr_dists = True) or actual distances (sqr_dists = False)\n between the provided coords and the nearest neighbouring points.\n If KD-tree has coordinate units and sqr_dists=False, distances will be unit-aware.\n Shape is (n_coords,) for k=1, or (n_coords, k) for k>1.\ni : array\n An integer array of indices into the `points` array (passed into the constructor)\n corresponding to the nearest neighbour for the search coordinates.\n Shape is (n_coords,) for k=1, or (n_coords, k) for k>1.", + "existing_docstring": "Adapt the mesh in place so cell sizes track ``|\u2207field|``, using\nthe validated optimal-transport reset pattern.\n\nEach call resets the mesh to a cached reference (the initial uniform\ncoordinates), FE-remaps ``field`` onto that clean canvas, builds a\ngradient-density metric, runs the OT mover, and FE-remaps the\nrequested fields onto the adapted positions. Resetting every event\n(rather than composing adaptations across time steps) is what keeps\nthe mover sliver-free over long runs. The \"reset\" is internal \u2014 from\nthe caller's point of view this just tracks the moving feature.\n\nTopology is preserved (vertex count, DOF maps, rank partition\nunchanged); only coordinates move. Registered solvers are marked for\nrebuild via ``_deform_mesh``.\n\nReference coordinates\n---------------------\nThe reset target is snapshotted lazily on the **first** call as\n``self._ot_adapt_reference_coords`` (a copy of the current\n``mesh.X.coords``) and reused thereafter.\n\n.. warning::\n If the mesh is deformed by something other than ``OT_adapt``\n between calls (e.g. a manual ``mesh._deform_mesh(...)`` or a\n resume that loads a *deformed* snapshot), the cached reference no\n longer matches the intended pristine state. Use\n :meth:`OT_adapt_reset_reference` to re-baseline, or pass an\n explicit ``reference_coords`` for a one-off override.\n\nParameters\n----------\nfield : MeshVariable\n Scalar field whose gradient drives refinement (typically ``T``).\n Always FE-remapped onto the adapted mesh.\nrefinement : float, default 3.0\n Cell-size envelope ``h0/refinement`` for the densest cells.\n Validated range 1.5\u20135; 3 is the Nu sweet spot.\ncoarsening : float or \"auto\", default \"auto\"\n ``\"auto\"`` = budget-conserving ``refinement**(1/d)``.\ngrad_smoothing_length : \"auto\", None, float, or Pint Quantity, default \"auto\"\n Screened-Poisson de-noising length for ``|\u2207field|`` before the\n metric is built \u2014 the most effective sliver lever; without it,\n production refinement chases sub-cell gradient noise.\n ``\"auto\"`` (default) \u2248 the mesh's uniform cell size (mean edge\n length) \u2014 the validated setting. ``None`` turns it off. A number\n or Pint length sets ``L`` explicitly; **user-supplied lengths are\n unit-aware** (non-dimensionalised via the projection), so pass a\n Pint quantity (or a non-dimensional number) \u2014 ``\u2248 h0`` is mild,\n ``\u2248 2\u00b7h0`` stronger.\nmetric_choice : {\"front-following\", \"gradient-uniform\"}, default \"front-following\"\nfields_to_remap : list of MeshVariable, optional\n Extra fields to FE-remap onto the adapted positions (``field`` is\n always remapped). ``None`` \u21d2 just ``field``.\nfields_to_zero : list of MeshVariable, optional\n Fields to zero after the adapt (e.g. ``[V, P]`` for a cold\n restart of the flow solve).\nskip_threshold : float, optional\n If the mesh is already aligned with the metric (misalignment\n below this; see :func:`~underworld3.meshing.mesh_metric_mismatch`),\n skip the adapt and return ``False``. ``None`` \u21d2 always adapt.\nreference_coords : array, optional\n One-off override of the reset target (does not update the cache).\nverbose : bool, default False\n\nReturns\n-------\nbool\n ``True`` if the mesh was adapted, ``False`` if the\n ``skip_threshold`` check short-circuited it.\n\nNotes\n-----\nBoundary nodes slide tangentially and stay on the boundary for\nradial coordinate systems (Annulus / shell), using the projected\nboundary normal ``mesh.Gamma_P1``. Cartesian boundaries are pinned\n(the vertex-evaluated normal is degenerate there).\n\nConstrained-manifold meshes (``mesh.dim != mesh.cdim``, e.g. a 2D\nspherical surface in 3D) are **not supported**: the OT mover would\nhave to constrain *every* node to the surface, not just boundary\nnodes. See ``docs/developer/design/ot-adapt-api-proposal.md``.\n\nExamples\n--------\n>>> mesh = uw.meshing.Annulus(radiusOuter=1.0, radiusInner=0.5,\n... cellSize=1/16, qdegree=3)\n>>> T = uw.discretisation.MeshVariable(\"T\", mesh, 1, degree=3)\n>>> # ... initialise T ...\n>>> mesh.OT_adapt(T, refinement=3.0, fields_to_remap=[T])\n\nSee Also\n--------\nOT_adapt_reset_reference : Re-baseline the reset reference coords.\nunderworld3.meshing.follow_metric : The single-shot anisotropic mover.\nadapt : Topology-changing MMG remeshing (different mechanism).", "harvested_comments": [ - "Convert coordinates to match tree's coordinate system", - "Query parent KD-tree (returns actual distances, not squared)", - "Handle distance units", - "Squared distances - dimensionless or have squared units", - "For now, return as plain array (squared units are complex to handle)" + "... initialise T ...", + "Lazy snapshot of the reset target on first call." ], "status": "complete", "needs": [], - "parent_class": "KDTree", + "parent_class": "Mesh", "is_public": true }, { - "name": "rbf_interpolator_local_from_kdtree", + "name": "OT_adapt_reset_reference", "kind": "method", - "file": "src/underworld3/kdtree.py", - "line": 203, - "signature": "(self, coords, data, nnn, p, verbose)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5883, + "signature": "(self, coords = None)", "parameters": [ { "name": "coords", "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "data", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "nnn", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "p", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Performs an inverse distance (squared) mapping of data to the target `coords`.\n\nThis method is unit-aware: if the KD-tree was built with unit-aware coordinates,\nit will automatically convert query coordinates to match before interpolation.\n\nParameters\n----------\ncoords : array-like\n The target spatial coordinates to evaluate the data from.\n Can be unit-aware (UnitAwareArray) or plain numpy array.\n If KD-tree has coordinate units, coords must have compatible units.\n coords.shape[1] == self.ndim\ndata : ndarray\n The known data to map from. Must be fully described over kd-tree.\n i.e., data.shape[0] == self.n\nnnn : int\n The number of neighbour points to sample from. If `1`, no distance averaging is done.\np : int\n The power index to calculate weights, i.e., pow(distance, -p)\nverbose : bool\n Print when mapping occurs\n\nReturns\n-------\nndarray\n Interpolated data values at target coordinates", - "harvested_comments": [ - "Convert coordinates to match tree's coordinate system", - "query nnn points to the coords using wrapped query function", - "distance_n is a list of distance to the nearest neighbours for all coords_contiguous", - "closest_n is the index of the neighbours from ncoords for all coords_contiguous", - "Note: We use the converted coordinates here, and query() will handle them properly" + "existing_docstring": "Re-baseline the reference coordinates used by :meth:`OT_adapt`.\n\n``coords=None`` re-snapshots the current ``mesh.X.coords`` as the new\nreset target; passing explicit ``coords`` (e.g. the initial uniform\nmesh loaded from a checkpoint) sets those instead. Use on resume,\nwhen the loaded mesh is in a deformed state and the cache would\notherwise lazily initialise from it.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": "KDTree", + "parent_class": "Mesh", "is_public": true }, { - "name": "set_property", + "name": "adapt", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 75, - "signature": "(self, prop: Union[MaterialProperty, str], value: Any)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5899, + "signature": "(self, metric_field, verbose = False)", "parameters": [ { - "name": "prop", - "type_hint": "Union[MaterialProperty, str]", + "name": "metric_field", + "type_hint": null, "default": null, "description": "" }, { - "name": "value", - "type_hint": "Any", - "default": null, + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Set a material property value", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Adapt the mesh discretization based on a metric field.\n\nThis method refines or coarsens the mesh in place, automatically\ntransferring all attached MeshVariables, updating Surfaces, and\nmarking Solvers for rebuild on their next solve() call.\n\nParameters\n----------\nmetric_field : MeshVariable\n A scalar MeshVariable containing metric values (1/h\u00b2 where h is\n target edge length). Larger values mean finer mesh (smaller elements).\n Use Surface.refinement_metric() to create this field from distance.\nverbose : bool, optional\n If True, print progress and statistics during adaptation.\n\nNotes\n-----\nThe adaptation uses PETSc's mesh adaptation with MMG/pragmatic backend.\n\n**What happens automatically:**\n\n- MeshVariables are interpolated to the new mesh\n- Surfaces recompute their distance fields\n- Swarms are marked as stale (particle-element associations invalidated)\n- Solvers are marked for rebuild (happens lazily on next solve())\n\nExamples\n--------\n>>> # Define metric from fault distance\n>>> metric = uw.discretisation.MeshVariable(\"H\", mesh, 1)\n>>> with mesh.access(metric):\n... # Smaller H near fault, larger far away\n... metric.data[:, 0] = 0.01 + 0.09 * fault.distance_from(mesh.data)\n>>> mesh.adapt(metric, verbose=True)\n>>> stokes.solve() # Solver rebuilds automatically", + "harvested_comments": [ + "Define metric from fault distance", + "Smaller H near fault, larger far away", + "Solver rebuilds automatically", + "Store old state for transfer", + "Notify surfaces to mark their distance fields as stale" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "MaterialDefinition", + "parent_class": "Mesh", "is_public": true }, { - "name": "get_property", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 80, - "signature": "(self, prop: Union[MaterialProperty, str], default = None)", + "name": "meshVariable_lookup_by_symbol", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 6517, + "signature": "(mesh, sympy_object)", "parameters": [ { - "name": "prop", - "type_hint": "Union[MaterialProperty, str]", + "name": "mesh", + "type_hint": null, "default": null, "description": "" }, { - "name": "default", + "name": "sympy_object", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Get a material property value", + "existing_docstring": "Given a sympy object, scan the mesh variables in `mesh` to find the\nlocation (meshvariable, component in the data array) corresponding to the symbol\nor return None if not found", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "MaterialDefinition", + "parent_class": null, "is_public": true }, { - "name": "has_property", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 85, - "signature": "(self, prop: Union[MaterialProperty, str]) -> bool", + "name": "petsc_dm_find_labeled_points_local", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 6534, + "signature": "(dm, label_name, label_value, sectionIndex = False, verbose = False)", "parameters": [ { - "name": "prop", - "type_hint": "Union[MaterialProperty, str]", + "name": "dm", + "type_hint": null, "default": null, "description": "" - } - ], - "returns": "bool", - "existing_docstring": "Check if material has a specific property", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "MaterialDefinition", - "is_public": true - }, - { - "name": "evaluate_property", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 90, - "signature": "(self, prop: Union[MaterialProperty, str], temperature = None, pressure = None)", - "parameters": [ + }, { - "name": "prop", - "type_hint": "Union[MaterialProperty, str]", + "name": "label_name", + "type_hint": null, "default": null, "description": "" }, { - "name": "temperature", + "name": "label_value", "type_hint": null, - "default": "None", + "default": null, "description": "" }, { - "name": "pressure", + "name": "sectionIndex", "type_hint": null, - "default": "None", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Evaluate a material property, accounting for temperature/pressure dependence.\n\nParameters:\n-----------\nprop : MaterialProperty or str\n Property to evaluate\ntemperature : float or array, optional\n Temperature for evaluation\npressure : float or array, optional\n Pressure for evaluation\n\nReturns:\n--------\nProperty value (scalar or array)", + "existing_docstring": "Identify local points associated with \"Label\"\n\ndm -> expects a petscDM object\nlabel_name -> \"String Name for Label\"\nsectionIndex -> False: leave points as indexed by the relevant section on the dm\n True: index into the local coordinate array\n\nNOTE: Assumes uniform element types", "harvested_comments": [ - "Get base value", - "Apply temperature dependence", - "Apply pressure dependence" + "print(f\"Label: {label_name} / {label_value}\")", + "print(f\"points: {pStart}: {pEnd}\")", + "print(f\"edges : {eStart}: {eEnd}\")", + "print(f\"faces : {fStart}: {fEnd}\")", + "print(f\"\", flush=True)" ], - "status": "complete", - "needs": [], - "parent_class": "MaterialDefinition", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, "is_public": true }, { - "name": "create_material", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 156, - "signature": "(self, name: str, description: str = '', reference: str = '') -> MaterialDefinition", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "description", - "type_hint": "str", - "default": "''", - "description": "" - }, - { - "name": "reference", - "type_hint": "str", - "default": "''", - "description": "" - } - ], - "returns": "MaterialDefinition", - "existing_docstring": "Create a new material definition.\n\nParameters:\n-----------\nname : str\n Material name\ndescription : str\n Human-readable description\nreference : str\n Literature reference\n\nReturns:\n--------\nMaterialDefinition\n New material instance", + "name": "units", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 482, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return the units associated with this variable.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "MaterialRegistry", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "get_material", + "name": "units", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 186, - "signature": "(self, name: str) -> Optional[MaterialDefinition]", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 487, + "signature": "(self, value)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "value", + "type_hint": null, "default": null, "description": "" } ], - "returns": "Optional[MaterialDefinition]", - "existing_docstring": "Get a material by name", + "returns": null, + "existing_docstring": "Set the units for this variable.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "MaterialRegistry", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "list_materials", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 190, - "signature": "(self) -> List[str]", + "name": "has_units", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 492, + "signature": "(self)", "parameters": [], - "returns": "List[str]", - "existing_docstring": "List all material names", + "returns": null, + "existing_docstring": "Check if this variable has units.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "MaterialRegistry", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "delete_material", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 194, - "signature": "(self, name: str)", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 497, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Delete a material definition", + "existing_docstring": "Get the dimensionality of this variable.", "harvested_comments": [ - "Remove any region assignments" + "Use Pint directly to get dimensionality" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "MaterialRegistry", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "assign_to_region", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 206, - "signature": "(self, material_name: str, region_id: int)", - "parameters": [ - { - "name": "material_name", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "region_id", - "type_hint": "int", - "default": null, - "description": "" - } - ], + "name": "remesh_policy", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 510, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Assign a material to a mesh region.\n\nParameters:\n-----------\nmaterial_name : str\n Name of material to assign\nregion_id : int\n Mesh region identifier", + "existing_docstring": "Per-variable transfer policy on a mesh adapt.\n\nSee :class:`underworld3.discretisation.remesh.RemeshPolicy`.\nDefault is :attr:`~underworld3.discretisation.remesh.RemeshPolicy.REMAP`\n(the safe Eulerian default \u2014 evaluate the old field at the new\nnode positions). Set to ``REINIT`` for stateless work-vars\n(gradient/Hessian projection targets, RBF proxies) and ``CARRY``\nonly for genuinely Lagrangian fields.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "MaterialRegistry", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "get_region_material", + "name": "clone", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 223, - "signature": "(self, region_id: int) -> Optional[str]", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 696, + "signature": "(self, name, varsymbol)", "parameters": [ { - "name": "region_id", - "type_hint": "int", + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "varsymbol", + "type_hint": null, "default": null, "description": "" } ], - "returns": "Optional[str]", - "existing_docstring": "Get the material assigned to a region", + "returns": null, + "existing_docstring": "Create a copy of this variable with new name and symbol.\n\nCreates a new mesh variable with the same mesh, shape, type,\ndegree, and continuity as this variable, but with a different\nname and symbolic representation.\n\nParameters\n----------\nname : str\n Name for the new variable.\nvarsymbol : str\n LaTeX symbol for the new variable.\n\nReturns\n-------\nMeshVariable\n New mesh variable with copied structure but independent data.", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "MaterialRegistry", + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "get_material_regions", + "name": "pack_raw_data_to_petsc", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 227, - "signature": "(self, material_name: str) -> List[int]", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 727, + "signature": "(self, data_array, sync = True)", "parameters": [ { - "name": "material_name", - "type_hint": "str", + "name": "data_array", + "type_hint": null, "default": null, "description": "" + }, + { + "name": "sync", + "type_hint": null, + "default": "True", + "description": "" } ], - "returns": "List[int]", - "existing_docstring": "Get all regions assigned to a material", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": "Pack data array to PETSc using traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\ndata_array : numpy.ndarray\n Array data in traditional flat format (-1, num_components)\nsync : bool\n Whether to sync parallel operations (default True)", + "harvested_comments": [ + "Convert to expected shape: (-1, num_components)", + "Direct PETSc access (following mesh.access pattern)", + "Ensure vector is available", + "Mark mesh DM as initialized (replaces old _accessed flag logic)", + "Direct assignment to PETSc vec (like mesh.access does at line 1156)" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "MaterialRegistry", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "evaluate_property_field", + "name": "pack_uw_data_to_petsc", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 235, - "signature": "(self, prop: Union[MaterialProperty, str], region_field: np.ndarray, temperature: Optional[np.ndarray] = None, pressure: Optional[np.ndarray] = None) -> np.ndarray", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 781, + "signature": "(self, data_array, sync = True)", "parameters": [ { - "name": "prop", - "type_hint": "Union[MaterialProperty, str]", + "name": "data_array", + "type_hint": null, "default": null, "description": "" }, { - "name": "region_field", - "type_hint": "np.ndarray", - "default": null, - "description": "" - }, - { - "name": "temperature", - "type_hint": "Optional[np.ndarray]", - "default": "None", - "description": "" - }, - { - "name": "pressure", - "type_hint": "Optional[np.ndarray]", - "default": "None", + "name": "sync", + "type_hint": null, + "default": "True", "description": "" } ], - "returns": "np.ndarray", - "existing_docstring": "Evaluate a material property over a field of region IDs.\n\nParameters:\n-----------\nprop : MaterialProperty or str\n Property to evaluate\nregion_field : array\n Array of region IDs\ntemperature : array, optional\n Temperature field for evaluation\npressure : array, optional\n Pressure field for evaluation\n\nReturns:\n--------\narray\n Property values corresponding to each region", + "returns": null, + "existing_docstring": "Enhanced pack method that directly accesses mesh data without access() context.\nDesigned for the new meshVariable.array interface.\n\nCRITICAL: This is the entry point where dimensional data enters PETSc storage.\nAll unit conversion and non-dimensionalization must happen here.\n\nParameters\n----------\ndata_array : numpy.ndarray or UWQuantity or UnitAwareArray\n Array data to pack into mesh field. Can have units (will be converted).\nsync : bool\n Whether to sync parallel operations (default True)", "harvested_comments": [ - "Initialize output array", - "Evaluate property for each unique region", - "Get mask for this region", - "Extract temperature/pressure for this region if provided", - "Evaluate property for this region" - ], - "status": "complete", - "needs": [], - "parent_class": "MaterialRegistry", - "is_public": true - }, - { - "name": "add_callback", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 293, - "signature": "(self, callback: Callable)", - "parameters": [ - { - "name": "callback", - "type_hint": "Callable", - "default": null, - "description": "" - } + "STEP 1: Handle unit conversion for incoming data", + "This is CRITICAL for the array setter to work correctly with units", + "Check if data has units (UWQuantity or UnitAwareArray)", + "Data has units - need to convert and extract magnitude", + "Convert incoming units to variable's units" ], - "returns": null, - "existing_docstring": "Add a callback function for material changes.\n\nParameters:\n-----------\ncallback : callable\n Function called as callback(event_type, *args)", - "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "MaterialRegistry", - "is_public": true - }, - { - "name": "export_config", - "kind": "method", - "file": "src/underworld3/materials.py", - "line": 312, - "signature": "(self) -> Dict[str, Any]", - "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "Export material configuration", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "MaterialRegistry", + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "import_config", + "name": "unpack_raw_data_from_petsc", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 327, - "signature": "(self, config: Dict[str, Any])", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 884, + "signature": "(self, squeeze = True, sync = True)", "parameters": [ { - "name": "config", - "type_hint": "Dict[str, Any]", - "default": null, + "name": "squeeze", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "sync", + "type_hint": null, + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": "Import material configuration from exported dict", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Unpack data from PETSc in traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\nsqueeze : bool\n Whether to remove singleton dimensions (default True)\nsync : bool\n Whether to sync parallel operations (default True)\n\nReturns\n-------\nnumpy.ndarray\n Array data in traditional flat format (-1, num_components)", + "harvested_comments": [ + "Direct PETSc access (following mesh.access pattern at line 1156)", + "Ensure vector is available", + "Mark mesh DM as initialized (replaces old _accessed flag logic)", + "Get data directly from PETSc vec (like mesh.access does)", + "Sync parallel operations if requested" ], - "parent_class": "MaterialRegistry", + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "create_standard_mantle_material", - "kind": "function", - "file": "src/underworld3/materials.py", - "line": 344, - "signature": "(registry: MaterialRegistry) -> MaterialDefinition", + "name": "unpack_uw_data_from_petsc", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 933, + "signature": "(self, squeeze = True, sync = True)", "parameters": [ { - "name": "registry", - "type_hint": "MaterialRegistry", - "default": null, + "name": "squeeze", + "type_hint": null, + "default": "True", "description": "" - } - ], - "returns": "MaterialDefinition", - "existing_docstring": "Create a standard mantle material with typical properties", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "create_standard_crust_material", - "kind": "function", - "file": "src/underworld3/materials.py", - "line": 361, - "signature": "(registry: MaterialRegistry) -> MaterialDefinition", - "parameters": [ + }, { - "name": "registry", - "type_hint": "MaterialRegistry", - "default": null, + "name": "sync", + "type_hint": null, + "default": "True", "description": "" } ], - "returns": "MaterialDefinition", - "existing_docstring": "Create a standard crustal material with typical properties", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "returns": null, + "existing_docstring": "Enhanced unpack method that directly accesses mesh data without access() context.\nDesigned for the new meshVariable.array interface.\n\nParameters\n----------\nsqueeze : bool\n Whether to remove singleton dimensions (default True)\nsync : bool\n Whether to sync parallel operations (default True)\n\nReturns\n-------\nnumpy.ndarray\n Array data in correct shape for the variable", + "harvested_comments": [ + "Direct PETSc access (following mesh.access pattern at line 1156)", + "Ensure vector is available", + "Mark mesh DM as initialized (replaces old _accessed flag logic)", + "Get data directly from PETSc vec (like mesh.access does)", + "Unpack data using same layout as original method" ], - "parent_class": null, + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "create_high_viscosity_material", - "kind": "function", - "file": "src/underworld3/materials.py", - "line": 378, - "signature": "(registry: MaterialRegistry, name: str = 'high_visc', viscosity_contrast: float = 1000) -> MaterialDefinition", + "name": "rbf_interpolate", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1008, + "signature": "(self, new_coords, meth = 0, p = 2, verbose = False, nnn = None, rubbish = None)", "parameters": [ { - "name": "registry", - "type_hint": "MaterialRegistry", + "name": "new_coords", + "type_hint": null, "default": null, "description": "" }, { - "name": "name", - "type_hint": "str", - "default": "'high_visc'", + "name": "meth", + "type_hint": null, + "default": "0", "description": "" }, { - "name": "viscosity_contrast", - "type_hint": "float", - "default": "1000", - "description": "" - } - ], - "returns": "MaterialDefinition", - "existing_docstring": "Create a high viscosity material for inclusion studies", - "harvested_comments": [ - "Base properties similar to mantle" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "delta", - "kind": "function", - "file": "src/underworld3/maths/functions.py", - "line": 21, - "signature": "(x: sympy.Basic, epsilon: float)", - "parameters": [ - { - "name": "x", - "type_hint": "sympy.Basic", - "default": null, + "name": "p", + "type_hint": null, + "default": "2", "description": "" }, { - "name": "epsilon", - "type_hint": "float", - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Smoothed (Gaussian) approximation to the Dirac delta function.\n\nReturns a Gaussian with integral 1, approximating :math:`\\delta(x)`\nas :math:`\\epsilon \\to 0`:\n\n.. math::\n\n \\delta_\\epsilon(x) = \\frac{1}{\\epsilon\\sqrt{2\\pi}}\n \\exp\\left(-\\frac{x^2}{2\\epsilon^2}\\right)\n\nParameters\n----------\nx : sympy.Basic\n Symbolic expression (typically a coordinate or distance function).\nepsilon : float\n Smoothing width. Smaller values give sharper peaks.\n\nReturns\n-------\nsympy.Expr\n Gaussian approximation to the delta function.\n\nNotes\n-----\nUseful for representing interfaces, point sources, or boundary layers\nin a regularized form suitable for finite element integration.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "L2_norm", - "kind": "function", - "file": "src/underworld3/maths/functions.py", - "line": 59, - "signature": "(n_s, a_s, mesh)", - "parameters": [ - { - "name": "n_s", + "name": "verbose", "type_hint": null, - "default": null, + "default": "False", "description": "" }, { - "name": "a_s", + "name": "nnn", "type_hint": null, - "default": null, + "default": "None", "description": "" }, { - "name": "mesh", + "name": "rubbish", "type_hint": null, - "default": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "L2 norm of the difference between numerical and analytical solutions.\n\nComputes:\n\n.. math::\n\n \\|n - a\\|_{L^2} = \\sqrt{\\int_\\Omega (n - a)^2 \\, d\\Omega}\n\nFor vector fields, uses the dot product:\n\n.. math::\n\n \\|\\mathbf{n} - \\mathbf{a}\\|_{L^2} = \\sqrt{\\int_\\Omega\n (\\mathbf{n} - \\mathbf{a}) \\cdot (\\mathbf{n} - \\mathbf{a}) \\, d\\Omega}\n\nParameters\n----------\nn_s : sympy.Expr or sympy.Matrix\n Numerical solution (scalar or vector field).\na_s : sympy.Expr or sympy.Matrix\n Analytical solution (scalar or vector field).\nmesh : Mesh\n The mesh over which to integrate.\n\nReturns\n-------\nfloat\n L2 norm of the error.", + "existing_docstring": "Interpolate variable data to new coordinates using RBF.\n\nUses inverse distance weighting with k-nearest neighbors to\ninterpolate values from mesh nodes to arbitrary coordinates.\n\nParameters\n----------\nnew_coords : numpy.ndarray\n Target coordinates of shape ``(n_points, dim)``.\nmeth : int, optional\n Interpolation method (reserved, currently unused).\np : float, optional\n Power parameter for inverse distance weighting (default: 2).\nverbose : bool, optional\n Print progress information.\nnnn : int, optional\n Number of nearest neighbors (default: 4 for 3D, 3 for 2D).\n\nReturns\n-------\nnumpy.ndarray\n Interpolated values at new coordinates.", "harvested_comments": [ - "Check if the input is a vector (SymPy Matrix)", - "Compute squared difference using dot product for vectors", - "Compute squared difference for scalars", - "Integral over the domain", - "Compute the L2 norm" + "An inverse-distance mapping is quite robust here ... as long", + "as long we take care of the case where some nodes coincide (likely if used mesh2mesh)", + "Use cached KDTree for interpolation" ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank2_symmetric_sym", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 45, - "signature": "(name, dim)", + "name": "save", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1055, + "signature": "(self, filename: str, name: Optional[str] = None, index: Optional[int] = None)", "parameters": [ { - "name": "name", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Rank 2 symmetric tensor (as symbolic matrix), name is a sympy latex expression", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "rank4_symmetric_sym", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 58, - "signature": "(name, dim)", - "parameters": [ { "name": "name", - "type_hint": null, - "default": null, + "type_hint": "Optional[str]", + "default": "None", "description": "" }, { - "name": "dim", - "type_hint": null, - "default": null, + "name": "index", + "type_hint": "Optional[int]", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Rank 4 symmetric tensor (as symbolic matrix), name is a sympy latex expression", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Append variable data to the specified mesh hdf5\ndata file. The file must already exist.\n\nParameters\n----------\nfilename :\n The filename of the mesh checkpoint file. It\n must already exist.\nname :\n Textual name for dataset. In particular, this\n will be used for XDMF generation. If not\n provided, the variable name will be used.\nindex :\n Not currently supported. An optional index which\n might correspond to the timestep (for example).", + "harvested_comments": [ + "Keep vector available for future access", + "Ensure global vector is up-to-date before writing.", + "# JM:To enable timestep recording, the following needs to be called.", + "# I'm unsure if the corresponding xdmf functionality is enabled via", + "# the PETSc xdmf script." + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "tensor_rotation", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 77, - "signature": "(R, T)", + "name": "write", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1136, + "signature": "(self, filename: str)", "parameters": [ { - "name": "R", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "T", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Rotate tensor of any rank using matrix R", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Write variable data to the specified mesh hdf5\ndata file. The file will be over-written.\n\nNote: This is a low-level method intended to be called by wrapper\nfunctions such as ``mesh.write_timestep()`` which handle output paths,\noptional XDMF generation, optional PETSc reload metadata, and\nmulti-variable coordination. Prefer using ``mesh.write_timestep()`` for\nmesh-variable output.\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.\n\nParameters\n----------\nfilename :\n The filename of the mesh checkpoint file", + "harvested_comments": [ + "Keep vector available for future access", + "Variable coordinates - let's put those in the file to", + "make it a standalone \"swarm\"", + "Ensure global vector is up-to-date before writing.", + "# Add variable unit metadata to standalone file" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank2_to_voigt", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 179, - "signature": "(v_ij, dim, covariant = True)", + "name": "read_timestep", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1239, + "signature": "(self, data_filename, data_name, index, outputPath = '', verbose = False)", "parameters": [ { - "name": "v_ij", + "name": "data_filename", "type_hint": null, "default": null, "description": "" }, { - "name": "dim", + "name": "data_name", "type_hint": null, "default": null, "description": "" }, { - "name": "covariant", - "type_hint": null, - "default": "True", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Convert rank-2 tensor :math:`v_{ij}` to Voigt (vector) form :math:`V_I`.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "voigt_to_rank2", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 197, - "signature": "(V_I, dim, covariant = True)", - "parameters": [ - { - "name": "V_I", + "name": "index", "type_hint": null, "default": null, "description": "" }, { - "name": "dim", + "name": "outputPath", "type_hint": null, - "default": null, + "default": "''", "description": "" }, { - "name": "covariant", + "name": "verbose", "type_hint": null, - "default": "True", + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Convert to rank 2 tensor (v_ij) from voigt (vector) form (V_I)", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Read a mesh variable from ``Mesh.write_timestep()`` output using the\ncoordinate-remap path. The saved mesh and the live mesh may have\ndifferent sizes or decompositions; values are matched to the live mesh\nnodes by nearest-neighbour KDTree interpolation.\n\nThis is the flexible remap reader. It is distinct from\n``read_checkpoint()``, which loads PETSc DMPlex section/vector metadata\nfor PETSc-native same-mesh reload.\n\nParallel-safe and memory-bounded. Two transient swarms route the\nwork without ever holding the full file on more than one rank:\n\n 1. **Source swarm** \u2014 rank 0 reads the file; saved\n ``(coord, value)`` pairs migrate to whichever rank owns the\n centroid-domain of each location.\n\n 2. **Query swarm** \u2014 each rank inserts *its own* live DOF\n coordinates. They migrate using the same centroid logic, so\n a live DOF and a saved point at the same coordinate land on\n the same rank regardless of how PETSc partitioned the DM.\n Each rank then runs a rank-local KDTree against the saved\n data it received, and the interpolated values migrate back to\n the live DOF's home rank.\n\nPer-rank memory is bounded by ``file_size / n_ranks`` rather than\n``file_size`` per rank.", + "harvested_comments": [ + "Format dispatch: ``data_filename`` may be either the", + "``write_timestep`` filename base (in which case we reconstruct the", + "per-variable file path the usual way) or a v1.1 snapshot wrapper path", + "produced by", + "``model.save_state(file=\u2026)``. The format-detection logic is" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank4_to_voigt", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 215, - "signature": "(c_ijkl, dim)", + "name": "read_checkpoint", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1486, + "signature": "(self, filename: str, data_name: Optional[str] = None)", "parameters": [ { - "name": "c_ijkl", - "type_hint": null, + "name": "filename", + "type_hint": "str", "default": null, "description": "" }, { - "name": "dim", - "type_hint": null, - "default": null, + "name": "data_name", + "type_hint": "Optional[str]", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Convert rank 4 tensor (c_ijkl) to voigt (matrix) form (C_IJ)", + "existing_docstring": "Load this mesh variable from PETSc reload output.\n\nThis is an exact PETSc DMPlex section/vector reload path. It does not\nuse the coordinate/KDTree remapping used by ``read_timestep()``. New\noutput should be written with ``Mesh.write_timestep(...,\npetsc_reload=True)``; legacy ``Mesh.write_checkpoint()`` files are also\nsupported.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "voigt_to_rank4", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 227, - "signature": "(C_IJ, dim)", - "parameters": [ - { - "name": "C_IJ", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Convert to rank 4 tensor (c_ijkl) from voigt (matrix) form (C_IJ)", + "name": "fn", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1570, + "signature": "(self) -> sympy.Basic", + "parameters": [], + "returns": "sympy.Basic", + "existing_docstring": "The handle to the (i,j,k) spatial view of this variable if it exists (deprecated)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank2_to_mandel", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 236, - "signature": "(v_ij, dim)", - "parameters": [ - { - "name": "v_ij", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Convert rank-2 tensor to Mandel vector form.\n\nMandel notation scales off-diagonal terms by :math:`\\sqrt{2}`,\npreserving inner products under vector operations.\n\nParameters\n----------\nv_ij : sympy.Matrix\n Symmetric rank-2 tensor as (dim x dim) matrix.\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.Matrix\n Mandel vector (3 components for 2D, 6 for 3D).", + "name": "ijk", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1577, + "signature": "(self) -> sympy.Basic", + "parameters": [], + "returns": "sympy.Basic", + "existing_docstring": "The handle to the (i,j,k) spatial view of this variable if it exists", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank4_to_mandel", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 262, - "signature": "(c_ijkl, dim)", - "parameters": [ - { - "name": "c_ijkl", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } + "name": "sym", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1584, + "signature": "(self) -> sympy.Basic", + "parameters": [], + "returns": "sympy.Basic", + "existing_docstring": "Symbolic representation for use in equations and expressions.\n\nReturns the SymPy Matrix representation of this variable, which can\nbe used in constitutive models, boundary conditions, and PDE definitions.\nThe symbolic form is used during equation assembly and JIT compilation.\n\nReturns\n-------\nsympy.Matrix\n Symbolic matrix with shape depending on variable type:\n - Scalar: (1, 1)\n - Vector: (dim, 1)\n - Tensor: (dim, dim)\n\nNotes\n-----\n- Unit scaling is applied during ``unwrap()``, not when accessing ``.sym``\n- For arithmetic operations, variables support direct use without ``.sym``\n (e.g., ``density * velocity`` works directly)\n\nExamples\n--------\n>>> # Use in constitutive model\n>>> stokes.constitutive_model.viscosity = viscosity.sym[0, 0]\n>>> # Use in boundary condition\n>>> solver.add_dirichlet_bc(temperature.sym[0, 0], \"Top\")\n>>> # Direct arithmetic (no .sym needed)\n>>> momentum = density * velocity # Works directly\n\nSee Also\n--------\nsym_1d : Flattened symbolic representation (Voigt notation for tensors).\narray : Numerical data access with unit handling.", + "harvested_comments": [ + "Use in constitutive model", + "Use in boundary condition", + "Direct arithmetic (no .sym needed)", + "Works directly", + "Note: Scaling is applied during unwrap(), not here" ], - "returns": null, - "existing_docstring": "Convert rank-4 tensor to Mandel matrix form.\n\nParameters\n----------\nc_ijkl : sympy.NDimArray\n Symmetric rank-4 tensor (dim x dim x dim x dim).\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.Matrix\n Mandel matrix (3x3 for 2D, 6x6 for 3D).", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "mandel_to_rank2", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 284, - "signature": "(v_I, dim)", - "parameters": [ - { - "name": "v_I", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Convert Mandel vector to rank-2 tensor form.\n\nParameters\n----------\nv_I : sympy.Matrix\n Mandel vector (3 components for 2D, 6 for 3D).\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.Matrix\n Symmetric rank-2 tensor as (dim x dim) matrix.", + "name": "sym_1d", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1624, + "signature": "(self) -> sympy.Basic", + "parameters": [], + "returns": "sympy.Basic", + "existing_docstring": "The handle to a flattened version of the sympy.Matrix view of this variable.\nAssume components are stored in the same order that sympy iterates entries in\na matrix except for the symmetric tensor case where we store in a Voigt form", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "mandel_to_rank4", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 305, - "signature": "(c_IJ, dim)", - "parameters": [ - { - "name": "c_IJ", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "mesh", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1897, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Convert Mandel matrix to rank-4 tensor form.\n\nParameters\n----------\nc_IJ : sympy.Matrix\n Mandel matrix (3x3 for 2D, 6x6 for 3D).\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.NDimArray\n Symmetric rank-4 tensor (dim x dim x dim x dim).", + "existing_docstring": "The mesh this variable belongs to (accessed via weak reference).\nRaises RuntimeError if the mesh has been garbage collected.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank4_identity", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 326, - "signature": "(dim)", - "parameters": [ - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } + "name": "vec", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1914, + "signature": "(self) -> PETSc.Vec", + "parameters": [], + "returns": "PETSc.Vec", + "existing_docstring": "The corresponding PETSc local vector for this variable.", + "harvested_comments": [ + "Ensure vector is initialized when accessed" ], - "returns": null, - "existing_docstring": "Symmetric fourth-order identity tensor.\n\nConstructs the identity tensor for symmetric second-order tensors:\n\n.. math::\n\n I_{ijkl} = \\frac{1}{2}(\\delta_{ik}\\delta_{jl} + \\delta_{il}\\delta_{jk})\n\nThis tensor satisfies :math:`I_{ijkl} \\sigma_{kl} = \\sigma_{ij}` for\nsymmetric :math:`\\sigma`.\n\nParameters\n----------\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.NDimArray\n Fourth-order identity tensor (dim x dim x dim x dim).", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "rank2_inner_product", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 360, - "signature": "(A, B)", - "parameters": [ - { - "name": "A", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "B", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Double contraction (inner product) of two rank-2 tensors.\n\nComputes:\n\n.. math::\n\n p = \\sum_i \\sum_j A_{ij} B_{ij} = A : B\n\nParameters\n----------\nA : sympy.Matrix or sympy.NDimArray\n First rank-2 tensor.\nB : sympy.Matrix or sympy.NDimArray\n Second rank-2 tensor.\n\nReturns\n-------\nsympy.Expr\n Scalar result of the double contraction.", + "name": "old_data", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1946, + "signature": "(self) -> numpy.ndarray", + "parameters": [], + "returns": "numpy.ndarray", + "existing_docstring": "TESTING: Original data property implementation.\nNumpy proxy array to underlying variable data.\nNote that the returned array is a proxy for all the *local* nodal\ndata, and is provided as 1d list.\n\nFor both read and write, this array can only be accessed via the\nmesh `access()` context manager.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "cross", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 67, - "signature": "(self, vector1, vector2)", - "parameters": [ - { - "name": "vector1", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "vector2", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "array", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1961, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Cross product of two vector fields.\n\nParameters\n----------\nvector1 : sympy.Matrix\n First vector as row matrix.\nvector2 : sympy.Matrix\n Second vector as row matrix.\n\nReturns\n-------\nsympy.Matrix\n Cross product :math:`\\mathbf{a} \\times \\mathbf{b}` as row matrix.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "existing_docstring": "Primary interface for reading and writing variable data.\n\nReturns a structured array view with shape ``(N, a, b)`` where ``N`` is the\nnumber of mesh nodes and ``(a, b)`` depends on the variable type:\n\n- Scalar: ``(N, 1, 1)``\n- Vector: ``(N, 1, dim)``\n- Tensor: ``(N, dim, dim)``\n- Symmetric tensor: ``(N, dim, dim)`` (symmetric storage)\n\nWhen the variable has units, values are automatically converted:\n\n- **Reading**: Returns values in physical units\n- **Writing**: Accepts physical units, converts to non-dimensional for storage\n\nReturns\n-------\nNDArray\n Array view that delegates changes back to canonical storage.\n\nExamples\n--------\n>>> # Scalar field initialization\n>>> temperature.array[:, 0, 0] = 300.0 # Set all nodes to 300\n\n>>> # Vector field initialization\n>>> velocity.array[:, 0, 0] = 1.0 # x-component\n>>> velocity.array[:, 0, 1] = 0.0 # y-component\n>>> # Or set entire vector at once\n>>> velocity.array[:, 0, :] = np.column_stack([vx, vy])\n\n>>> # Coordinate-based initialization\n>>> temperature.array[:, 0, 0] = 1000 + 500 * mesh.X.coords[:, 0]\n\n>>> # Reading values\n>>> max_temp = temperature.array[:, 0, 0].max()\n\nNotes\n-----\nThis property is a view of the canonical ``.data`` property with\nautomatic shape conversion. All modifications are synchronized\nwith PETSc storage.\n\nSee Also\n--------\ndata : Flat format ``(-1, components)`` for variable-to-variable transfers.\nsym : Symbolic representation for equations.\ncoords : Spatial coordinates of data points.", + "harvested_comments": [ + "Scalar field initialization", + "Set all nodes to 300", + "Vector field initialization", + "x-component", + "y-component" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "curl", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 91, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "data", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2690, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Curl of a vector field: :math:`\\nabla \\times \\mathbf{v}`.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix.\n\nReturns\n-------\nsympy.Matrix or sympy.Expr\n In 3D: curl vector as row matrix.\n In 2D: scalar (out-of-plane component, i.e., vorticity).", + "existing_docstring": "Canonical data storage in flat format for internal operations.\n\nReturns data in shape ``(-1, num_components)`` regardless of variable type.\nValues are always **non-dimensional** (no unit conversion applied).\n\nThis property is the canonical storage that handles PETSc synchronization.\nThe ``.array`` property is a view of this with shape conversion.\n\nWhen to Use\n-----------\n- **Variable-to-variable transfers**: Copying data between variables that\n both operate in non-dimensional space avoids redundant unit conversions\n- **Low-level PETSc operations**: Direct access to solver data\n- **Backward compatibility**: Existing code using flat format\n\nFor general user access, prefer ``.array`` which handles units and provides\na structured shape.\n\nReturns\n-------\nNDArray_With_Callback\n Array with shape ``(-1, num_components)`` with automatic PETSc sync.\n\nExamples\n--------\n>>> # Efficient variable-to-variable copy (no unit conversion)\n>>> new_temperature.data[...] = old_temperature.data[...]\n\n>>> # Check data shape\n>>> scalar_var.data.shape # (N, 1)\n>>> vector_var.data.shape # (N, dim)\n\nSee Also\n--------\narray : Structured format ``(N, a, b)`` with automatic unit handling.\nsym : Symbolic representation for equations.", "harvested_comments": [ - "if 2d, the out-of-plane vector is not defined in the basis so a scalar is returned (cf. vorticity)" + "Efficient variable-to-variable copy (no unit conversion)", + "Check data shape", + "Cache validation: the cached array is a numpy view into _lvec.array.", + "If _lvec has been destroyed and recreated (e.g. DM rebuild when new", + "variables are added, or mesh adaptation), the cached view becomes a" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "divergence", + "name": "array", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 116, - "signature": "(self, matrix)", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2818, + "signature": "(self, array_value)", "parameters": [ { - "name": "matrix", + "name": "array_value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Divergence of a vector field: :math:`\\nabla \\cdot \\mathbf{v}`.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "existing_docstring": "Set variable data using pack method to handle shape transformation.", + "harvested_comments": [ + "Use pack method to handle proper data transformation and shape conversion" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "gradient", + "name": "min", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 134, - "signature": "(self, scalar)", - "parameters": [ - { - "name": "scalar", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2872, + "signature": "(self) -> Union[float, tuple]", + "parameters": [], + "returns": "Union[float, tuple]", + "existing_docstring": "The global variable minimum value.\nReturns the value only (not the rank). For multi-component variables,\nreturns a tuple of minimum values for each component.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", + "harvested_comments": [ + "Sync local\u2192global to ensure global vector has latest data", + "Get raw non-dimensional values from PETSc", + "Dimensionalise using units system" ], - "returns": null, - "existing_docstring": "Gradient of a scalar field: :math:`\\nabla \\phi`.\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "strain_tensor", + "name": "max", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 154, - "signature": "(self, vector)", - "parameters": [ - { - "name": "vector", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Symmetric gradient (strain or strain-rate tensor).\n\nComputes the infinitesimal strain tensor from displacement,\nor strain-rate tensor from velocity:\n\n.. math::\n\n \\varepsilon_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)\n\nParameters\n----------\nvector : sympy.Matrix or sympy.vector.Vector\n Displacement or velocity field.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (dim x dim) strain tensor.", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2900, + "signature": "(self) -> Union[float, tuple]", + "parameters": [], + "returns": "Union[float, tuple]", + "existing_docstring": "The global variable maximum value.\nReturns the value only (not the rank). For multi-component variables,\nreturns a tuple of maximum values for each component.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", "harvested_comments": [ - "Coerce vector to sympy.Matrix form" + "Sync local\u2192global to ensure global vector has latest data", + "Get raw non-dimensional values from PETSc", + "Dimensionalise using units system" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "to_vector", + "name": "sum", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 184, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Convert row matrix to SymPy vector form.\n\nParameters\n----------\nmatrix : sympy.Matrix or sympy.vector.Vector\n Vector as row or column matrix.\n\nReturns\n-------\nsympy.vector.Vector\n Vector in the mesh's coordinate system basis.", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2928, + "signature": "(self) -> Union[float, tuple]", + "parameters": [], + "returns": "Union[float, tuple]", + "existing_docstring": "The global variable sum value.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", "harvested_comments": [ - "No need to convert", - "Note, the mesh vector basis is always 3D so out-of-plane", - "vectors are allowed." + "Sync local\u2192global to ensure global vector has latest data", + "Get raw non-dimensional values from PETSc", + "Dimensionalise using units system" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "to_matrix", + "name": "norm", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 216, - "signature": "(self, vector)", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2956, + "signature": "(self, norm_type) -> Union[float, tuple]", "parameters": [ { - "name": "vector", + "name": "norm_type", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Convert SymPy vector to row matrix form.\n\nParameters\n----------\nvector : sympy.vector.Vector or sympy.Matrix\n Vector to convert.\n\nReturns\n-------\nsympy.Matrix\n Row matrix (1 x dim) representation.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "returns": "Union[float, tuple]", + "existing_docstring": "The global variable norm value.\n\nnorm_type: type of norm, one of\n - 0: NORM 1 ||v|| = sum_i | v_i |. ||A|| = max_j || v_*j ||\n - 1: NORM 2 ||v|| = sqrt(sum_i |v_i|^2) (vectors only)\n - 3: NORM INFINITY ||v|| = max_i |v_i|. ||A|| = max_i || v_i* ||, maximum row sum\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", + "harvested_comments": [ + "Sync local\u2192global to ensure global vector has latest data", + "Get raw non-dimensional values from PETSc", + "Dimensionalise using units system" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "jacobian", + "name": "mean", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 247, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2989, + "signature": "(self) -> Union[float, tuple]", + "parameters": [], + "returns": "Union[float, tuple]", + "existing_docstring": "The global variable mean value.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", + "harvested_comments": [ + "Sync local\u2192global to ensure global vector has latest data", + "Get raw non-dimensional values from PETSc", + "Dimensionalise using units system" ], - "returns": null, - "existing_docstring": "Jacobian matrix of a field with respect to mesh coordinates.\n\nParameters\n----------\nmatrix : sympy.Matrix or sympy.vector.Vector\n Scalar, vector, or matrix field.\n\nReturns\n-------\nsympy.Matrix\n Jacobian matrix of partial derivatives.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "divergence", + "name": "std", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 302, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Divergence of a vector field in cylindrical coordinates.\n\nComputes :math:`\\nabla \\cdot \\mathbf{v}` with the cylindrical\nmetric terms:\n\n.. math::\n\n \\nabla \\cdot \\mathbf{v} = \\frac{\\partial v_r}{\\partial r}\n + \\frac{v_r}{r} + \\frac{1}{r}\\frac{\\partial v_\\theta}{\\partial \\theta}\n + \\frac{\\partial v_z}{\\partial z}\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_z)`.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence.", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3016, + "signature": "(self) -> Union[float, tuple]", + "parameters": [], + "returns": "Union[float, tuple]", + "existing_docstring": "The global variable standard deviation value.\n\nWhen units are enabled (model.has_units() == True), returns UWQuantity\nwith proper dimensionality.", "harvested_comments": [ - "Or is this cdim ?" + "Sync local\u2192global to ensure global vector has latest data", + "Get raw values from PETSc", + "For scalar: std = sqrt((sum(x^2)/n) - (sum(x)/n)^2)", + "Create a temporary vector for x^2 computation", + "Calculate variance: E[x^2] - (E[x])^2" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_cylindrical", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "gradient", + "name": "stats", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 340, - "signature": "(self, scalar)", - "parameters": [ - { - "name": "scalar", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3072, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Gradient of a scalar field in cylindrical coordinates.\n\nComputes :math:`\\nabla \\phi` with the cylindrical metric:\n\n.. math::\n\n \\nabla \\phi = \\frac{\\partial \\phi}{\\partial r}\\hat{r}\n + \\frac{1}{r}\\frac{\\partial \\phi}{\\partial \\theta}\\hat{\\theta}\n + \\frac{\\partial \\phi}{\\partial z}\\hat{z}\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix.", + "existing_docstring": "Universal statistics method for all variable types.\n\nReturns various statistical measures appropriate for the variable type.\nFor scalars: standard statistical measures.\nFor vectors: magnitude-based statistics.\nFor tensors: Frobenius norm and invariant-based measures.\n\nReturns\n-------\ndict\n Dictionary containing statistical measures:\n - 'type': Variable type ('scalar', 'vector', 'tensor')\n - 'components': Number of components\n - 'size': Number of elements\n - 'mean': Mean value (scalar) or magnitude mean (vector/tensor)\n - 'min': Minimum value (scalar) or magnitude min (vector/tensor)\n - 'max': Maximum value (scalar) or magnitude max (vector/tensor)\n - 'sum': Sum of all values\n - 'norm2': L2 norm\n - 'rms': Root mean square\n\n Additional keys for vectors/tensors:\n - 'magnitude_*': Statistics on vector magnitude\n - 'frobenius_*': Statistics on tensor Frobenius norm (for tensors)\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.", "harvested_comments": [ - "Or is this cdim ?" + "This uses a private work MeshVariable and the various norms defined there but", + "could either be simplified to just use petsc vectors, or extended to", + "compute integrals over the elements which is in line with uw1 and uw2" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_cylindrical", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "curl", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 380, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Curl of a vector field in cylindrical coordinates.\n\nComputes :math:`\\nabla \\times \\mathbf{v}` with cylindrical metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_z)`.\n\nReturns\n-------\nsympy.Matrix or sympy.Expr\n In 3D: curl vector as row matrix.\n In 2D: scalar (out-of-plane vorticity component).", + "name": "coords", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3238, + "signature": "(self) -> numpy.ndarray", + "parameters": [], + "returns": "numpy.ndarray", + "existing_docstring": "Spatial coordinates of this variable's degree-of-freedom locations.\n\nReturns coordinates for this variable's specific DOF locations, which may\ndiffer from mesh vertex locations if the variable degree differs from the\nmesh coordinate degree.\n\nWhen the mesh has reference quantities set, returns dimensional coordinates\nin physical units. Otherwise returns non-dimensional model coordinates.\n\nReturns\n-------\nndarray or UnitAwareArray\n Coordinates with shape ``(N, dim)`` where ``N`` is the number of DOFs.\n Returns unit-aware array if mesh has units configured.\n\nExamples\n--------\n>>> # Get DOF coordinates for initialization\n>>> x_coords = temperature.coords[:, 0]\n>>> temperature.array[:, 0, 0] = 300 + 100 * x_coords\n\n>>> # Pass to evaluation functions\n>>> values = uw.function.evaluate(expression, var.coords)\n\nSee Also\n--------\ncoords_nd : Non-dimensional coordinates for internal operations.\nmesh.X.coords : Mesh vertex coordinates (may differ from DOF coords).", "harvested_comments": [ - "if 2D, return a scalar of the out-of-plane curl" + "Get DOF coordinates for initialization", + "Pass to evaluation functions", + "Get non-dimensional [0-1] model coordinates for this variable's specific DOF locations", + "If mesh has units, dimensionalise to physical coordinates", + "Only dimensionalise if model has reference quantities set" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_cylindrical", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "strain_tensor", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 420, - "signature": "(self, vector)", - "parameters": [ - { - "name": "vector", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Strain tensor in cylindrical coordinates.\n\nComputes the symmetric gradient with cylindrical metric corrections.\nAdditional terms arise from coordinate curvature.\n\nParameters\n----------\nvector : sympy.Matrix\n Displacement or velocity field :math:`(v_r, v_\\theta, v_z)`.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (dim x dim) strain tensor with cylindrical corrections.", + "name": "coords_nd", + "kind": "property", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3300, + "signature": "(self) -> numpy.ndarray", + "parameters": [], + "returns": "numpy.ndarray", + "existing_docstring": "Non-dimensional [0-1] coordinates for this variable's DOF locations.\n\nReturns raw model coordinates from PETSc without any unit wrapping.\nThis is the coordinate system used by internal KDTree indexing, evaluation,\nand other algorithmic operations.\n\nFor user-facing operations with physical units, use `.coords` which returns\ndimensional coordinates when the model has reference quantities set.\n\nReturns\n-------\nndarray\n Non-dimensional [0-1] coordinates, shape (N, dim)\n\nExamples\n--------\n>>> # Internal algorithmic use - KDTree indexing\n>>> kd_tree = uw.kdtree.KDTree(var.coords_nd)\n>>>\n>>> # User-facing display with dimensional units\n>>> print(f\"Positions: {var.coords}\") # Shows meters, km, etc.\n\nNotes\n-----\nThis is a zero-copy operation that returns a view of the cached coordinate\narray directly from the mesh. No memory allocation or copying occurs.\n\nSee Also\n--------\ncoords : Dimensional coordinates with unit wrapping (user-facing)", "harvested_comments": [ - "Coerce vector to sympy.Matrix form", - "E_00, E_22 and E_02 are unchanged from Cartesian", - "E[0,0] = L[0,0]" + "Internal algorithmic use - KDTree indexing", + "User-facing display with dimensional units", + "Shows meters, km, etc.", + "Direct access to non-dimensional coordinates from mesh cache", + "This is a ZERO-COPY operation - returns cached array directly" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_cylindrical", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { "name": "divergence", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 628, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3340, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Divergence of a vector field in spherical coordinates.\n\nComputes :math:`\\nabla \\cdot \\mathbf{v}` with spherical metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_\\phi)`.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence.", - "harvested_comments": [ - "cosecant_th = sympy.Piecewise(", - "(1000, sympy.Abs(t) < 0.01 * sympy.pi), (1 / sympy.sin(t), True)" + "existing_docstring": "Divergence of this variable: :math:`\\nabla \\cdot \\mathbf{v}`.\n\nUses the mesh's coordinate-aware vector calculus operators,\nwhich correctly handle cylindrical, spherical, or other\nnon-Cartesian coordinate systems.\n\nReturns\n-------\nsympy.Expr or None\n Scalar divergence expression, or None if the operation fails.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical", + "parent_class": "_BaseMeshVariable", "is_public": true }, { "name": "gradient", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 667, - "signature": "(self, scalar)", - "parameters": [ - { - "name": "scalar", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3357, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Gradient of a scalar field in spherical coordinates.\n\nComputes :math:`\\nabla \\phi` with spherical metric terms.\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix :math:`(\\partial_r, \\partial_\\theta/r, \\partial_\\phi/(r\\sin\\theta))`.", - "harvested_comments": [ - "grad_S[2] = sympy.Piecewise(", - "(1000, sympy.Abs(p) < 0.01 * sympy.pi),", - "(scalar.diff(p) / (r * sympy.sin(t)), True)," + "existing_docstring": "Gradient of this variable: :math:`\\nabla u`.\n\nUses the mesh's coordinate-aware vector calculus operators.\n\nReturns\n-------\nsympy.Matrix or None\n Gradient vector as row matrix, or None if the operation fails.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical", + "parent_class": "_BaseMeshVariable", "is_public": true }, { "name": "curl", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 704, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3372, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Curl of a vector field in spherical coordinates.\n\nComputes :math:`\\nabla \\times \\mathbf{v}` with spherical metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_\\phi)`.\n\nReturns\n-------\nsympy.Matrix\n Curl vector as row matrix.", + "existing_docstring": "Curl of this variable: :math:`\\nabla \\times \\mathbf{v}`.\n\nUses the mesh's coordinate-aware vector calculus operators.\n\nReturns\n-------\nsympy.Matrix, sympy.Expr, or None\n Curl vector (3D) or scalar vorticity (2D), or None if fails.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_BaseMeshVariable", "is_public": true }, { - "name": "strain_tensor", + "name": "jacobian", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 739, - "signature": "(self, vector)", - "parameters": [ - { - "name": "vector", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3387, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Jacobian matrix of this variable.\n\nComputes partial derivatives with respect to mesh coordinates:\n:math:`J_{ij} = \\partial v_i / \\partial x_j`.\n\nReturns\n-------\nsympy.Matrix\n Jacobian matrix of shape (var_dim, mesh_dim).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "_BaseMeshVariable", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 244, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Strain tensor in spherical coordinates.\n\nComputes the symmetric gradient with spherical metric corrections\nfor :math:`(r, \\theta, \\phi)` coordinates.\n\nParameters\n----------\nvector : sympy.Matrix\n Displacement or velocity field :math:`(v_r, v_\\theta, v_\\phi)`.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (3 x 3) strain tensor with spherical corrections.", + "existing_docstring": "Direct access to data array (supports += and other assignment ops).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "array", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 249, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Direct access to array (delegates to base variable).\n\nThe base variable's array implementation now handles units and\nnon-dimensional scaling correctly, so we just delegate to it.\nThis eliminates code duplication and ensures consistency.", "harvested_comments": [ - "Coerce vector to sympy.Matrix form" + "Simply delegate to the base variable's array property", + "The base variable now has the complete, working implementation" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "divergence", + "name": "array", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 819, - "signature": "(self, matrix)", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 262, + "signature": "(self, value)", "parameters": [ { - "name": "matrix", + "name": "value", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Divergence of a vector field on a spherical surface.\n\nComputes :math:`\\nabla \\cdot \\mathbf{v}` with surface metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_\\lambda, v_\\phi)`.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence on the surface.", + "existing_docstring": "Set array values.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "gradient", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 854, - "signature": "(self, scalar)", - "parameters": [ - { - "name": "scalar", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Gradient of a scalar field on a spherical surface.\n\nComputes :math:`\\nabla \\phi` on the unit sphere.\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix :math:`(\\partial_\\lambda/\\cos\\phi, \\partial_\\phi)`.", + "name": "name", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 269, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Variable name.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "curl", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 887, - "signature": "(self, matrix)", - "parameters": [ - { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - } + "name": "clean_name", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 274, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Cleaned variable name for PETSc.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "mesh", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 279, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Curl on a spherical surface.\n\nComputes the scalar vorticity (out-of-surface curl component)\non the unit sphere.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_\\lambda, v_\\phi)`.\n\nReturns\n-------\nsympy.Expr\n Scalar vorticity (radial component of curl).", + "existing_docstring": "The mesh this variable is defined on.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "strain_tensor", - "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 920, - "signature": "(self, vector)", - "parameters": [ - { - "name": "vector", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "coords", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 287, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Strain tensor on a spherical surface.\n\nComputes the symmetric gradient on the unit sphere with\nsurface metric corrections.\n\nParameters\n----------\nvector : sympy.Matrix\n Displacement or velocity field :math:`(v_\\lambda, v_\\phi)`.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (2 x 2) strain tensor on the surface.", - "harvested_comments": [ - "Coerce vector to sympy.Matrix form", - "E_00, E_22 and E_02 are unchanged from Cartesian" + "existing_docstring": "Coordinate array from base variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "QuarterAnnulus", - "kind": "function", - "file": "src/underworld3/meshing/annulus.py", - "line": 33, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, angle: float = 45, cellSize: float = 0.1, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", - "parameters": [ - { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "angle", - "type_hint": "float", - "default": "45", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "centre", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "qdegree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "name": "coords_nd", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 292, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Create a quarter-annulus (wedge) mesh in 2D.\n\nGenerates a pie-slice shaped mesh bounded by inner and outer circular\narcs and two radial edges. Provides :math:`(r, \\theta)` coordinates\nfor convenient representation of radial problems.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the annulus. Supports UWQuantity objects for\n automatic unit conversion.\nradiusInner : float, default=0.547\n Inner radius of the annulus. Set to 0 for a disc sector\n extending to the centre.\nangle : float, default=45\n Angular extent of the wedge in degrees. The wedge spans from\n ``-angle`` to ``+angle`` (total sweep = 2 * angle).\ncellSize : float, default=0.1\n Target mesh element size.\ncentre : bool, default=False\n If True and ``radiusInner=0``, mark the centre point as a\n boundary for applying point constraints.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner arc at :math:`r = r_{inner}`\n - ``Upper``: Outer arc at :math:`r = r_{outer}`\n - ``Left``: Left radial edge\n - ``Right``: Right radial edge\n - ``Centre``: Centre point (if :math:`r_{inner} = 0`)\n\nSee Also\n--------\nAnnulus : Full 360-degree annulus.\nSegmentofAnnulus : Partial annulus with arbitrary angle.\n\nExamples\n--------\nCreate a quarter-disc (no inner hole):\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.QuarterAnnulus(\n... radiusOuter=1.0,\n... radiusInner=0.0,\n... angle=45,\n... cellSize=0.05\n... )\n\nNotes\n-----\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: tangential direction :math:`(\\theta)`", - "harvested_comments": [ - "Convert unit-aware quantities to non-dimensional units", - "This enables: QuarterAnnulus(radiusOuter=uw.quantity(6370, \"km\"), ...)", - "angle is in degrees, not a length - don't convert", - "gmsh.model.geo.rotate([(p2, p3)], 0, 0, 0, 0, 0.3, 0, math.pi / 2)", - "add boundary normal information to the new mesh" + "existing_docstring": "Non-dimensional coordinates from base variable (for internal KDTree operations).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "Annulus", - "kind": "function", - "file": "src/underworld3/meshing/annulus.py", - "line": 273, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, cellSizeOuter: float = None, cellSizeInner: float = None, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", - "parameters": [ - { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "cellSizeOuter", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "cellSizeInner", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "centre", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "qdegree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "refinement", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } + "name": "num_components", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 297, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Number of components.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "shape", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 302, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Create a full 2D annulus mesh.\n\nGenerates a ring-shaped mesh (or disc if ``radiusInner=0``) using\ntriangular elements. Provides :math:`(r, \\theta)` coordinates with\nradial boundary normal directions.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the annulus. Supports UWQuantity objects.\nradiusInner : float, default=0.547\n Inner radius of the annulus. Set to 0 for a full disc.\ncellSize : float, default=0.1\n Default target mesh element size.\ncellSizeOuter : float, optional\n Element size at the outer boundary. Defaults to ``cellSize``.\ncellSizeInner : float, optional\n Element size at the inner boundary. Defaults to ``cellSize``.\n Use different sizes to create graded meshes.\ncentre : bool, default=False\n If True and ``radiusInner=0``, mark the centre point as a\n boundary for applying point constraints.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply. Each level\n approximately quadruples element count.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner boundary at :math:`r = r_{inner}`\n - ``Upper``: Outer boundary at :math:`r = r_{outer}`\n - ``Centre``: Centre point (if :math:`r_{inner} = 0`)\n\n The mesh includes a refinement callback that snaps boundary\n nodes back to true circular geometry after refinement.\n\nSee Also\n--------\nQuarterAnnulus : Partial annulus with radial edges.\nAnnulusInternalBoundary : Annulus with an internal interface.\nSphericalShell : 3D equivalent for spherical geometries.\n\nExamples\n--------\nCreate a standard annulus for mantle convection:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.Annulus(\n... radiusOuter=1.0,\n... radiusInner=0.547,\n... cellSize=0.05\n... )\n\nCreate a graded mesh (finer at inner boundary):\n\n>>> mesh = uw.meshing.Annulus(\n... radiusOuter=1.0,\n... radiusInner=0.5,\n... cellSizeOuter=0.1,\n... cellSizeInner=0.02\n... )\n\nNotes\n-----\nThe inner radius default of 0.547 corresponds approximately to the\nEarth's core-mantle boundary radius ratio (3480/6371).\n\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: tangential direction :math:`(\\theta)`\n\nFor free-slip boundary conditions on vector problems (e.g., Stokes),\nuse a penalty on the normal velocity component::\n\n Gamma_N = mesh.Gamma # discrete normal, or\n Gamma_N = mesh.CoordinateSystem.unit_e_0 # analytic radial\n stokes.add_natural_bc(\n penalty * Gamma_N.dot(v_soln.sym) * Gamma_N, \"Upper\"\n )", - "harvested_comments": [ - "discrete normal, or", - "analytic radial", - "Convert unit-aware quantities to non-dimensional units", - "This enables: Annulus(radiusOuter=uw.quantity(6370, \"km\"), ...)", - "l1 = gmsh.model.geo.add_line(p5, p4)" + "existing_docstring": "Variable shape.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "SegmentofAnnulus", - "kind": "function", - "file": "src/underworld3/meshing/annulus.py", - "line": 545, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, angleExtent: float = 45, cellSize: float = 0.1, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "name": "vtype", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 307, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Variable type.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "degree", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 312, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Polynomial degree.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 319, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Units for this variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 324, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Check if this variable has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 329, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get dimensionality (delegates to DimensionalityMixin or base variable).", + "harvested_comments": [ + "DimensionalityMixin.dimensionality uses self.units, which now delegates to _base_var", + "This ensures consistency" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "units_repr", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 335, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return string representation with units information.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "non_dimensional_value", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 342, + "signature": "(self, model = None)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "angleExtent", - "type_hint": "float", - "default": "45", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "centre", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "qdegree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "refinement", + "name": "model", "type_hint": null, "default": "None", "description": "" - }, - { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Generates a segment of an annulus using Gmsh. This function creates a 2D mesh of an annular segment defined by outer and inner radii,\nand the extent of the angle. The mesh can be customized with various parameters like cell size, element degree, and verbosity.\n\nParameters:\n-----------\nradiusOuter : float, optional\n The outer radius of the annular segment. Default is 1.0.\n\nradiusInner : float, optional\n The inner radius of the annular segment. Default is 0.547.\n\nangleExtent : float, optional\n The angular extent of the segment in degrees. Default is 45.\n\ncellSize : float, optional\n The target size for the mesh elements. This controls the density of the mesh. Default is 0.1.\n\ncentre : bool, optional\n If True, the segment will be centered at the origin. If False, the segment is positioned based on the radii. Default is False.\n\ndegree : int, optional\n The polynomial degree of the finite elements used in the mesh. Default is 1.\n\nqdegree : int, optional\n The quadrature degree for integration. Higher values may improve accuracy but increase computation time. Default is 2.\n\nfilename : str, optional\n The name of the file where the mesh will be saved. If None, a default name is generated based on the parameters. Default is None.\n\nrefinement : optional\n Refinement level or method for the mesh. Used to increase the resolution of the mesh in certain regions. Default is None.\n\ngmsh_verbosity : int, optional\n Controls the verbosity of Gmsh output. Set to 0 for minimal output, higher numbers for more detailed logs. Default is 0.\n\nverbose : bool, optional\n If True, the function prints additional information during execution. Default is False.\n\nReturns:\n--------\nNone\n The function generates and saves a mesh file according to the specified parameters.\n\nExample:\n--------\nmesh = uw.meshing.SegmentofAnnulus(\n radiusOuter=2.0,\n radiusInner=1.0,\n angleExtent=90.0,\n cellSize=0.05,\n centre=True,\n degree=2,\n qdegree=3,\n filename=\"custom_annulus_segment.msh\",\n gmsh_verbosity=1,\n verbose=True\n)", + "existing_docstring": "Get non-dimensionalized values of the variable.\n\nReturns the variable's data array in non-dimensional form based on\nthe model's reference quantities.", "harvested_comments": [ - "Convert unit-aware quantities to non-dimensional units", - "angleExtent is in degrees, not a length - don't convert", - "error checks", - "angle Extent in radian", - "gmsh.model.mesh.embed(0, [p0], 2, s) # not sure use of this line" + "Use provided model or get default", + "Just return the data divided by scaling coefficient", + "No scaling, return data as-is" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "AnnulusWithSpokes", - "kind": "function", - "file": "src/underworld3/meshing/annulus.py", - "line": 802, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSizeOuter: float = 0.1, cellSizeInner: float = None, centre: bool = False, spokes: int = 3, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "name": "continuous", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 363, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Whether variable is continuous.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "remesh_policy", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 368, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Per-variable transfer policy on a mesh adapt.\n\nDelegates to the base variable so ``mesh.vars[...]`` (which\nholds the base instances) and the user-facing wrapper agree on\nthe same policy.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "symbol", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 398, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Variable symbol for LaTeX representation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "vec", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 405, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "PETSc vector (for solver integration).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "sym", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 426, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Enhanced mathematical symbol.", + "harvested_comments": [ + "Get base symbol from underlying variable" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "pack_uw_data_to_petsc", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 438, + "signature": "(self, *args, **kwargs)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "cellSizeOuter", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "cellSizeInner", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "centre", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "spokes", - "type_hint": "int", - "default": "3", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "qdegree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "refinement", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "gmsh_verbosity", + "name": "*args", "type_hint": null, - "default": "0", + "default": null, "description": "" }, { - "name": "verbose", + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create an annulus mesh divided into sectors by radial spokes.\n\nGenerates a ring-shaped mesh with radial internal boundaries (spokes)\nthat divide the annulus into equal angular sectors. Useful for problems\nrequiring sector-wise analysis or periodic boundary conditions.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the annulus.\nradiusInner : float, default=0.547\n Inner radius of the annulus.\ncellSizeOuter : float, default=0.1\n Element size at the outer boundary.\ncellSizeInner : float, optional\n Element size at the inner boundary. Defaults to ``cellSizeOuter``.\ncentre : bool, default=False\n Mark the centre point as a boundary (if radiusInner=0).\nspokes : int, default=3\n Number of radial spokes dividing the annulus. Creates ``spokes``\n equal angular sectors.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner boundary at :math:`r = r_{inner}`\n - ``LowerPlus``: Inner boundary + spokes (for BCs)\n - ``Upper``: Outer boundary at :math:`r = r_{outer}`\n - ``UpperPlus``: Outer boundary + spokes (for BCs)\n - ``Spokes``: Radial spoke boundaries\n\nSee Also\n--------\nAnnulus : Standard annulus without spokes.\n\nExamples\n--------\nCreate a 6-sector annulus:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.AnnulusWithSpokes(\n... radiusOuter=1.0,\n... radiusInner=0.5,\n... spokes=6,\n... cellSizeOuter=0.05\n... )\n\nNotes\n-----\nThe ``LowerPlus`` and ``UpperPlus`` boundaries are composite labels\nthat include both the curved boundaries and the spoke edges, useful\nfor applying no-slip conditions that include radial walls.", - "harvested_comments": [ - "Convert unit-aware quantities to non-dimensional units", - "spokes is a count, not a length - don't convert", - "Now copy / rotate", - "We finally generate and save the mesh:", - "We need to build the plex here in order to make some changes" + "existing_docstring": "Pack data to PETSc format.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "AnnulusInternalBoundary", - "kind": "function", - "file": "src/underworld3/meshing/annulus.py", - "line": 1132, - "signature": "(radiusOuter: float = 1.5, radiusInternal: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, cellSize_Outer: float = None, cellSize_Inner: float = None, cellSize_Internal: float = None, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", + "name": "unpack_uw_data_from_petsc", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 442, + "signature": "(self, *args, **kwargs)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.5", - "description": "" - }, - { - "name": "radiusInternal", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "cellSize_Outer", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "cellSize_Inner", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "cellSize_Internal", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "centre", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "qdegree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "gmsh_verbosity", + "name": "*args", "type_hint": null, - "default": "0", + "default": null, "description": "" }, { - "name": "verbose", + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create an annulus mesh with a circular internal boundary.\n\nGenerates an annular mesh with an embedded internal boundary at a\nspecified radius, useful for tracking flux across interfaces or\ndefining different material layers.\n\nParameters\n----------\nradiusOuter : float, default=1.5\n Outer radius of the annulus.\nradiusInternal : float, default=1.0\n Radius of the internal boundary surface.\nradiusInner : float, default=0.547\n Inner radius of the annulus.\ncellSize : float, default=0.1\n Default target mesh element size.\ncellSize_Outer : float, optional\n Element size at the outer boundary.\ncellSize_Inner : float, optional\n Element size at the inner boundary.\ncellSize_Internal : float, optional\n Element size at the internal boundary.\ncentre : bool, default=False\n Mark the centre point as a boundary (if radiusInner=0).\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner boundary at :math:`r = r_{inner}`\n - ``Internal``: Internal boundary at :math:`r = r_{internal}`\n - ``Upper``: Outer boundary at :math:`r = r_{outer}`\n - ``Centre``: Centre point (if radiusInner=0)\n\nSee Also\n--------\nAnnulus : Annulus without internal boundary.\nBoxInternalBoundary : Cartesian box with internal boundary.\nSphericalShellInternalBoundary : 3D spherical equivalent.\n\nExamples\n--------\nCreate an annulus with an internal tracking surface:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.AnnulusInternalBoundary(\n... radiusOuter=1.0,\n... radiusInternal=0.7,\n... radiusInner=0.5,\n... cellSize=0.03\n... )\n\nNotes\n-----\nThe internal boundary is useful for calculating radial heat or\nmass flux across a specified radius (e.g., core-mantle boundary,\nthermal boundary layer).", - "harvested_comments": [ - "Convert unit-aware quantities to non-dimensional units", - "## adding this curve loop results in the mesh not being generated correctly", - "## although the internal boundary is still defined in the mesh dm", - "loops = [cl2] + loops", - "Outermost mesh" + "existing_docstring": "Unpack data from PETSc format.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": null, + "parent_class": "EnhancedMeshVariable", "is_public": true }, { - "name": "DiscInternalBoundaries", - "kind": "function", - "file": "src/underworld3/meshing/annulus.py", - "line": 1414, - "signature": "(radiusUpper: float = 1.5, radiusInternal: float = 1.0, radiusLower: float = 0.547, cellSize: float = 0.1, cellSize_Upper: float = None, cellSize_Lower: float = None, cellSize_Internal: float = None, cellSize_Centre: float = None, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", - "parameters": [ - { - "name": "radiusUpper", - "type_hint": "float", - "default": "1.5", - "description": "" - }, - { - "name": "radiusInternal", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusLower", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "cellSize_Upper", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "cellSize_Lower", - "type_hint": "float", - "default": "None", - "description": "" - }, - { - "name": "cellSize_Internal", - "type_hint": "float", + "name": "divergence", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 448, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Vector divergence calculation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "gradient", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 452, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Vector gradient calculation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "curl", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 456, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Vector curl calculation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "jacobian", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 460, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Vector jacobian calculation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "clone", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 466, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Clone the variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "max", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 470, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Maximum value of the variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "mean", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 474, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mean value of the variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "min", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 478, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Minimum value of the variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "norm", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 482, + "signature": "(self, norm_type = None)", + "parameters": [ + { + "name": "norm_type", + "type_hint": null, "default": "None", "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute the norm of the variable.", + "harvested_comments": [ + "Mathematical usage: use SymPy Matrix norm from MathematicalMixin", + "Computational usage: delegate to PETSc norm" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "load_from_h5_plex_vector", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 491, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" }, { - "name": "cellSize_Centre", - "type_hint": "float", - "default": "None", + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Load from HDF5 plex vector.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "read_checkpoint", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 495, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Load from a PETSc DMPlex checkpoint file.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "write", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 499, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Write variable data to HDF5 file.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "sym_1d", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 504, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Flattened sympy view of the variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "save", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 508, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, "description": "" }, { - "name": "filename", + "name": "**kwargs", "type_hint": null, - "default": "None", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Save variable data.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "read_timestep", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 512, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, "description": "" }, { - "name": "gmsh_verbosity", + "name": "**kwargs", "type_hint": null, - "default": "0", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Read timestep data.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "copy_into", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 516, + "signature": "(self, target)", + "parameters": [ + { + "name": "target", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Copy this variable's data into a variable on a related mesh.\n\nDetects the parent/submesh relationship and calls restrict or\nprolongate as appropriate. Both meshes must be related via\n``extract_region``.\n\nParameters\n----------\ntarget : MeshVariable\n Destination variable. Must be on the parent or a submesh\n of this variable's mesh.\n\nExamples\n--------\n>>> v_full.copy_into(v_rock) # restrict: parent \u2192 submesh\n>>> v_rock.copy_into(v_full) # prolongate: submesh \u2192 parent", + "harvested_comments": [ + "restrict: parent \u2192 submesh", + "prolongate: submesh \u2192 parent", + "target is submesh of source \u2192 restrict", + "source is submesh of target \u2192 prolongate" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "add_into", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 550, + "signature": "(self, target)", + "parameters": [ + { + "name": "target", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add this variable's data into a variable on a related mesh.\n\nLike ``copy_into`` but uses ADD_VALUES \u2014 adds to existing\nvalues in the target rather than replacing them.\n\nParameters\n----------\ntarget : MeshVariable\n Destination variable. Must be on the parent or a submesh\n of this variable's mesh.\n\nExamples\n--------\n>>> v_rock.add_into(v_full) # prolongate with ADD", + "harvested_comments": [ + "prolongate with ADD" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "stats", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 579, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, "description": "" }, { - "name": "verbose", + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get statistics for the variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "sum", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 583, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Sum of variable values.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "rbf_interpolate", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 587, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "RBF interpolation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "pack_raw_data_to_petsc", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 595, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pack raw data to PETSc format.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "unpack_raw_data_from_petsc", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 599, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create a disc mesh with multiple concentric internal boundaries.\n\nGenerates a full disc (extending to centre) with two embedded\ncircular internal boundaries at specified radii. Useful for\nmulti-layer problems or tracking flux across multiple interfaces.\n\nParameters\n----------\nradiusUpper : float, default=1.5\n Outer radius of the disc.\nradiusInternal : float, default=1.0\n Radius of the middle internal boundary.\nradiusLower : float, default=0.547\n Radius of the inner internal boundary.\ncellSize : float, default=0.1\n Default target mesh element size.\ncellSize_Upper : float, optional\n Element size at the outer boundary.\ncellSize_Lower : float, optional\n Element size at the lower internal boundary.\ncellSize_Internal : float, optional\n Element size at the middle internal boundary.\ncellSize_Centre : float, optional\n Element size at the disc centre.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner internal boundary at :math:`r = r_{lower}`\n - ``Internal``: Middle internal boundary at :math:`r = r_{internal}`\n - ``Upper``: Outer boundary at :math:`r = r_{upper}`\n - ``Centre``: Centre point\n\nSee Also\n--------\nAnnulusInternalBoundary : Annulus with one internal boundary.\n\nExamples\n--------\nCreate a disc with two tracking surfaces:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.DiscInternalBoundaries(\n... radiusUpper=1.0,\n... radiusInternal=0.7,\n... radiusLower=0.4,\n... cellSize=0.03\n... )\n\nNotes\n-----\nUnlike AnnulusInternalBoundary, this mesh extends to the centre\n(no inner hole). All internal boundaries are embedded as mesh\nedges, allowing flux integration across each interface.", - "harvested_comments": [ - "Convert unit-aware quantities to non-dimensional units", - "loops = [cl1] + loops", - "Outermost mesh", - "# Now add embedded surfaces", - "# This is the same as the simple annulus" + "existing_docstring": "Unpack raw data from PETSc format.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "field_id", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 606, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Field ID in the mesh.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "fn", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 611, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Function representation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "ijk", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 616, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "IJK coordinates.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "instance_number", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 621, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Instance number.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "old_data", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 626, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Legacy data property (for testing).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "uw_object_counter", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 631, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Object counter from API tools.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "transfer_data_from", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 637, + "signature": "(self, source_var)", + "parameters": [ + { + "name": "source_var", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Transfer data from another variable using global_evaluate.\n\nThis enables persistent variable identity across mesh changes.\n\nNote on Symbol Disambiguation (2025-12-15):\n This method transfers DATA, not symbolic expressions.\n The source and target variables have distinct symbolic identities\n (source_var.sym != self.sym) even if they share the same name.\n\n For transferring expressions containing coordinates, use explicit\n substitution: expr.subs({old_mesh.N.x: new_mesh.N.x, ...})\n\n See: docs/developer/design/SYMBOL_DISAMBIGUATION_2025-12.md\n\nArgs:\n source_var: Source variable to transfer data from\n\nReturns:\n bool: True if transfer successful", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "qualified_name", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 668, + "signature": "(self) -> Optional[str]", + "parameters": [], + "returns": "Optional[str]", + "existing_docstring": "Qualified name for collision resolution.\n\nReturns the fully qualified name used in the model registry\nto resolve namespace collisions (e.g., 'velocity_mesh123456').", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "is_persistent", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 678, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Whether this variable has persistence capabilities.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 702, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Display detailed information about the enhanced variable including units.\n\nShows variable name, dimensions, shape, units information, and sample data.", + "harvested_comments": [ + "Units information", + "Persistence information", + "Sample data (first few elements)", + "Mathematical capabilities", + "Allow chaining" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "EnhancedMeshVariable", + "is_public": true + }, + { + "name": "create_enhanced_mesh_variable", + "kind": "function", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 743, + "signature": "(varname: Union[str, list], mesh: 'Mesh', num_components: Union[int, tuple] = None, persistent: bool = True, units: Optional[str] = None, **kwargs) -> EnhancedMeshVariable", + "parameters": [ + { + "name": "varname", + "type_hint": "Union[str, list]", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "num_components", + "type_hint": "Union[int, tuple]", + "default": "None", + "description": "" + }, + { + "name": "persistent", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "units", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "EnhancedMeshVariable", + "existing_docstring": "Factory function to create an enhanced mesh variable.\n\nThis is a convenience function that creates an EnhancedMeshVariable\nwith persistence enabled by default.\n\nArgs:\n varname: Variable name\n mesh: Mesh object\n num_components: Number of components\n persistent: Enable persistence (default True)\n units: Units specification\n **kwargs: Additional arguments\n\nReturns:\n EnhancedMeshVariable instance", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "demonstrate_enhanced_variables", + "kind": "function", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 807, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Demonstration of enhanced variables with units and mathematical operations.\n\nThis function shows how the enhanced variables integrate mathematical\noperations with units checking and dimensional analysis.", + "harvested_comments": [ + "Create a simple mesh", + "Create enhanced mesh variables with units", + "Test mathematical operations", + "Component access", + "Vector operations" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "total_disp", + "kind": "property", + "file": "src/underworld3/discretisation/remesh.py", + "line": 127, + "signature": "(self) -> np.ndarray", + "parameters": [], + "returns": "np.ndarray", + "existing_docstring": "Total node displacement across the whole mover sweep (new \u2212 old).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "RemeshContext", + "is_public": true + }, + { + "name": "remesh_with_field_transfer", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 218, + "signature": "(mesh: 'Mesh', do_move: Callable[[], None]) -> bool", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "do_move", + "type_hint": "Callable[[], None]", + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Adapt-op contract: run a mover and transfer every registered var.\n\n``do_move`` is a closure that performs the actual coordinate move \u2014\ntypically the body of a ``_winslow_*`` mover, the OT step, or a\n``follow_metric`` mover. It is expected to call\n:meth:`Mesh._deform_mesh` one or more times and leave the mesh\nsitting at the final adapted positions. ``do_move`` MUST NOT touch\nfield ``.data`` \u2014 the helper owns transfer.\n\nThe helper:\n\n1. Snapshots the current coordinates (``old_X``) and the current\n ``.data`` of every REMAP variable.\n2. Calls ``do_move()`` to update mesh coords (REMAP-vars'\n ``.data`` is unchanged because ``_deform_mesh`` does not touch\n it; only coordinate-keyed caches are invalidated).\n3. Captures the new DOF coordinates for every REMAP var\n (``var.coords`` on the new mesh).\n4. Performs ONE deform-back \u2192 ``global_evaluate`` \u2192 deform-forward\n pair for the entire REMAP set, writing the resampled values back\n into each var's ``.data``.\n5. Fires every registered ``on_remesh(ctx)`` hook (Phase 2 ALE\n operators use this; Phase 1 hooks are typically no-ops).\n6. Zeros ``extra_zero`` vars (caller's REINIT-with-zero list, e.g.\n ``[V, P]`` for a cold-restart of the flow solve).\n7. Marks REINIT vars stale (Phase 1: not strictly required because\n the only REINIT-stamped vars today are recomputed on first\n access; placeholder for future eager invalidation).\n\nReturns ``True`` if the mesh actually moved (``do_move`` reported\ngeometry change); ``False`` if it short-circuited (caller can skip\ntransfer too). Detection: compares ``mesh.X.coords`` before and\nafter ``do_move``.\n\nParallel: ``global_evaluate`` resolves off-rank target points\ncorrectly, so the transfer is partition-agnostic \u2014 the rank that\nowns a new-mesh DOF coordinate retrieves its value from whichever\nrank held that point on the old mesh. Local ``evaluate`` would\nleave stale values at the partition seams (the documented failure\nmode that motivated this design).", + "harvested_comments": [ + "Re-entrancy guard: composite adapt ops (OT_adapt) wrap the whole", + "reset+build+smooth pipeline once at the outer level; inner movers", + "(smooth_mesh_interior called from inside that pipeline) consult", + "this flag and skip their own wrap.", + "Surface the outer scratch dict so a nested do_move can still" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "with_units", + "kind": "function", + "file": "src/underworld3/function/__init__.py", + "line": 98, + "signature": "(sympy_expr, name = None, units = None)", + "parameters": [ + { + "name": "sympy_expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Wrap a SymPy expression in a unit-aware object with .units and .to() methods.\n\nThis is particularly useful for derivatives and other SymPy operations that\nreturn plain SymPy expressions instead of unit-aware objects.\n\nParameters\n----------\nsympy_expr : sympy expression\n The SymPy expression to wrap (e.g., from temperature.diff(y))\nname : str, optional\n Optional name for the expression (auto-generated if not provided)\nunits : str, optional\n Explicit units for the expression. If provided, these units are used\n instead of trying to extract units from the expression. This is useful\n for derivatives where units are known from the derivative operation.\n\nReturns\n-------\nUWexpression\n Unit-aware expression with .units and .to() methods\n\nExamples\n--------\n>>> # Derivative returns plain SymPy\n>>> dTdy_sympy = temperature.diff(y)[0]\n>>>\n>>> # Wrap it to get unit-aware object\n>>> dTdy = uw.with_units(dTdy_sympy)\n>>> dTdy.units # kelvin / kilometer\n>>> dTdy.to(\"K/mm\") # Unit conversion works!\n\n>>> # Also works with other SymPy operations\n>>> combined = uw.with_units(2 * temperature.sym + pressure.sym)\n\n>>> # Explicit units for derivatives\n>>> dTdy_elem = uw.with_units(derivative_expr, units=\"kelvin / kilometer\")", + "harvested_comments": [ + "Derivative returns plain SymPy", + "Wrap it to get unit-aware object", + "kelvin / kilometer", + "Unit conversion works!", + "Also works with other SymPy operations" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "derivative", + "kind": "function", + "file": "src/underworld3/function/__init__.py", + "line": 166, + "signature": "(expression, variable, evaluate = True)", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "variable", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evaluate", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Obtain symbolic derivatives of any underworld function, correctly handling sub-expressions / constants.\n\nUWCoordinates from mesh.X work transparently with this function and with\nsympy.diff() directly - both approaches now produce identical results:\n\n x, y = mesh.X\n result = uw.function.derivative(expr, y) # This function\n result = sympy.diff(expr, y) # Also works (since Dec 2025)\n\nArgs:\n expression: The expression to differentiate\n variable: The variable to differentiate with respect to (UWCoordinate or BaseScalar)\n evaluate (bool): If True, evaluate immediately. If False, return deferred derivative.\n\nReturns:\n The derivative (evaluated SymPy expression or UWDerivativeExpression)", + "harvested_comments": [ + "This function", + "Also works (since Dec 2025)", + "Note: Since December 2025, UWCoordinate subclasses BaseScalar with __eq__/__hash__", + "that match the original N.x/N.y/N.z, so no conversion is needed anymore.", + "sympy.diff() works directly with UWCoordinates." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "create_structure", + "kind": "method", + "file": "src/underworld3/function/_dminterp_wrapper.pyx", + "line": 79, + "signature": "def create_structure(self, mesh, np.ndarray[double, ndim=2] coords,\n np.ndarray[long, ndim=1] cells, int dofcount):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Create and set up the DMInterpolation structure.\n\n This does the expensive DMInterpolationCreate + SetUp operations.\n\n Parameters\n ----------\n mesh : Mesh\n The mesh object\n coords : ndarray (n_points, dim)\n Coordinates to interpolate at\n cells : ndarray (n_points,)\n Cell hints for each coordinate\n dofcount : int\n Total number of DOFs to interpolate\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate", + "kind": "method", + "file": "src/underworld3/function/_dminterp_wrapper.pyx", + "line": 175, + "signature": "def evaluate(self, mesh, np.ndarray[double, ndim=2] outarray):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Evaluate interpolation using this cached structure.\n\n This is the FAST operation - just evaluation, no setup!\n\n Parameters\n ----------\n mesh : Mesh\n The mesh object (must call update_lvec() first!)\n outarray : ndarray (n_points, dofcount)\n Output array to fill with interpolated values\n\n Returns\n -------\n outarray : ndarray\n Same array, now filled with values\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "fdiff", + "kind": "method", + "file": "src/underworld3/function/_function.pyx", + "line": 77, + "signature": "def fdiff(self, argindex):", + "parameters": [], + "returns": null, + "existing_docstring": "\n SymPy protocol: Return derivative with respect to the argindex-th argument.\n\n This is an internal method called by SymPy's differentiation machinery\n (e.g., ``sympy.diff(expr, x)``). Users should not call this directly.\n\n By implementing this method, we control how Underworld mesh variables\n are differentiated symbolically, and ensure the resulting derivative\n objects can be compiled by the JIT system in ``_jitextension.py``.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "fdiff", + "kind": "method", + "file": "src/underworld3/function/_function.pyx", + "line": 130, + "signature": "def fdiff(self, argindex):", + "parameters": [], + "returns": null, + "existing_docstring": "SymPy protocol: Second derivatives are not supported.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "global_evaluate_nd", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 350, + "signature": "def global_evaluate_nd( expr,\n coords=None,\n coord_sys=None,\n other_arguments=None,\n simplify=True,\n verbose=False,\n evalf=False,\n rbf=False,\n data_layout=None,\n check_extrapolated=False,\n force_l2=False,\n smoothing=1e-6,\n local_fallback=True,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Internal: Parallel-safe expression evaluation (Cython implementation).\n\n This is the low-level Cython implementation for MPI-parallel evaluation.\n Users should typically use :func:`underworld3.function.global_evaluate`\n which provides automatic unit handling and a cleaner interface.\n\n Contract: this is a faithful *parallel* counterpart of :func:`evaluate` \u2014\n a query point is interpolated wherever in the mesh it lands (on any rank),\n a point just outside the mesh is extrapolated from its nearest cell (the\n globally nearest by a centroid-distance heuristic, with the lowest rank as\n the tie-break in parallel),\n and ``check_extrapolated`` returns an inside/outside flag per point. The\n result is independent of the number of ranks (up to the rank-local\n extrapolation residual near partition seams). Points that no rank can\n locate in-cell are resolved by a best-claim reduction over ranks (see the\n out-of-domain block below); pass ``local_fallback=False`` to restore the\n legacy behaviour where such points returned silently-wrong values. The\n ``GE_LOCAL_FALLBACK`` environment variable, if set, overrides the kwarg\n (an operator escape hatch retained from the parallel-deadlock debugging\n history; the kwarg is the supported control surface).\n\n Note it is not efficient to call this function to evaluate an expression at\n a single coordinate. Instead the user should provide a numpy array of all\n coordinates requiring evaluation.\n\n See Also\n --------\n underworld3.function.global_evaluate : User-facing parallel evaluation.\n\n Parameters\n ----------\n expr: sympy.Basic\n Sympy expression requiring evaluation.\n coords: numpy.ndarray, list, or tuple\n Coordinates to evaluate expression at. Can be:\n - numpy array of doubles (shape: n_points x n_dims)\n - list/tuple of tuples with unit-aware coordinates: [(x1, y1), (x2, y2), ...]\n - list/tuple for single point: [x, y] or [x, y, z]\n Coordinate values can be UWQuantity, pint.Quantity, or numeric (float/int).\n Unit-aware coordinates are automatically converted to SI base units.\n coord_sys: mesh.N vector coordinate system\n\n other_arguments: dict\n Dictionary of other arguments necessary to evaluate function.\n Not yet implemented.\n\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate_nd", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 901, + "signature": "def evaluate_nd( expr,\n coords=None,\n coord_sys=None,\n other_arguments=None,\n simplify=True,\n verbose=False,\n evalf=False,\n rbf=False,\n data_layout=None,\n check_extrapolated=False,\n force_l2=False,\n smoothing=1e-6):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Internal: Evaluate expression at coordinates (Cython implementation).\n\n This is the low-level Cython implementation. Users should typically use\n :func:`underworld3.function.evaluate` which provides automatic unit\n handling and a cleaner interface.\n\n Note it is not efficient to call this function to evaluate an expression at\n a single coordinate. Instead the user should provide a numpy array of all\n coordinates requiring evaluation.\n\n See Also\n --------\n underworld3.function.evaluate : User-facing function with unit support.\n\n Parameters\n ----------\n expr: sympy.Basic\n Sympy expression requiring evaluation.\n coords: numpy.ndarray, list, or tuple\n Coordinates to evaluate expression at. Can be:\n - numpy array of doubles (shape: n_points x n_dims)\n - list/tuple of tuples with unit-aware coordinates: [(x1, y1), (x2, y2), ...]\n - list/tuple for single point: [x, y] or [x, y, z]\n Coordinate values can be UWQuantity, pint.Quantity, or numeric (float/int).\n Unit-aware coordinates are automatically converted to SI base units.\n coord_sys: mesh.N vector coordinate system\n\n other_arguments: dict\n Dictionary of other arguments necessary to evaluate function.\n Not yet implemented.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "petsc_interpolate", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 1091, + "signature": "def petsc_interpolate( expr,\n np.ndarray coords=None,\n coord_sys=None,\n mesh=None,\n other_arguments=None,\n simplify=True,\n verbose=False, ):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Evaluate a given expression at a list of coordinates.\n\n Note it is not efficient to call this function to evaluate an expression at\n a single coordinate. Instead the user should provide a numpy array of all\n coordinates requiring evaluation.\n\n Parameters\n ----------\n expr: sympy.Basic\n Sympy expression requiring evaluation.\n coords: numpy.ndarray\n Numpy array of coordinates to evaluate expression at.\n coord_sys: mesh.N vector coordinate system\n\n other_arguments: dict\n Dictionary of other arguments necessary to evaluate function.\n Not yet implemented.\n\n Notes\n -----\n This function leverages Sympy's `lambdify` function to provide efficient\n expression evaluation. It operates as follows:\n 1. Extract all Underworld variables functions from the expression. Note that\n all variables functions must be leaf nodes of the corresponding expression\n tree, as the variable function arguments must simply be the coordinate\n vector `mesh.r`. This is a necessary requirement to avoid complication in the\n domain decomposed parallel runtime situation, where a modified variable function\n argument (such as `mesh.r - (10,0)`) might translate the variable function onto\n a neighbouring subdomain. Handling this would result in great complication and\n inefficiency, and we therefore disallow it.\n 2. Each variable function is evaluated at the user provided coordinates to generate\n an array of evaluated results.\n 3. Replace all variable function instances within the expression with sympy\n symbol placeholders.\n 4. Generate a Sympy lambdified expression. This expression takes as arguments the\n user provided coordinates, and the Underworld variable function placeholders.\n 5. Evaluate the generated lambdified expresson using the coordinate array and\n evaluated variable function result arrays.\n 6. Return results array for full expression evaluation.\n\n\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "interpolate_vars_on_mesh", + "kind": "method", + "file": "src/underworld3/function/_function.pyx", + "line": 1222, + "signature": "def interpolate_vars_on_mesh( varfns, np.ndarray coords ):", + "parameters": [], + "returns": null, + "existing_docstring": "\n This function performs the interpolation for the given variables\n on a single mesh.\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rbf_evaluate", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 1381, + "signature": "def rbf_evaluate( expr,\n coords=None,\n coord_sys=None,\n mesh=None,\n other_arguments=None,\n verbose=False,\n simplify=True,):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Evaluate a given expression at a list of coordinates.\n\n Note it is not efficient to call this function to evaluate an expression at\n a single coordinate. Instead the user should provide a numpy array of all\n coordinates requiring evaluation.\n\n Parameters\n ----------\n expr: sympy.Basic\n Sympy expression requiring evaluation.\n coords: numpy.ndarray\n Numpy array of coordinates to evaluate expression at.\n coord_sys: mesh.N vector coordinate system\n\n other_arguments: dict\n Dictionary of other arguments necessary to evaluate function.\n Not yet implemented.\n\n Notes\n -----\n This function leverages Sympy's `lambdify` function to provide efficient\n expression evaluation. It operates as follows:\n 1. Extract all Underworld variables functions from the expression. Note that\n all variables functions must be leaf nodes of the corresponding expression\n tree, as the variable function arguments must simply be the coordinate\n vector `mesh.r`. This is a necessary requirement to avoid complication in the\n domain decomposed parallel runtime situation, where a modified variable function\n argument (such as `mesh.r - (10,0)`) might translate the variable function onto\n a neighbouring subdomain. Handling this would result in great complication and\n inefficiency, and we therefore disallow it.\n 2. Each variable function is evaluated at the user provided coordinates to generate\n an array of evaluated results.\n 3. Replace all variable function instances within the expression with sympy\n symbol placeholders.\n 4. Generate a Sympy lambdified expression. This expression takes as arguments the\n user provided coordinates, and the Underworld variable function placeholders.\n 5. Evaluate the generated lambdified expresson using the coordinate array and\n evaluated variable function result arrays.\n 6. Return results array for full expression evaluation.\n\n\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "dm_swarm_get_migrate_type", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 1503, + "signature": "def dm_swarm_get_migrate_type(swarm):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Get the migration type for a PETSc DMSwarm.\n\n The migration type controls how particles are transferred between\n MPI ranks during swarm migration operations.\n\n Parameters\n ----------\n swarm : Swarm\n The Underworld swarm object.\n\n Returns\n -------\n PETSc.DMSwarm.MigrateType\n The current migration type setting.\n\n See Also\n --------\n dm_swarm_set_migrate_type : Set the migration type.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "dm_swarm_set_migrate_type", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 1528, + "signature": "def dm_swarm_set_migrate_type(swarm, mtype):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Set the migration type for a PETSc DMSwarm.\n\n The migration type controls how particles are transferred between\n MPI ranks during swarm migration operations.\n\n Parameters\n ----------\n swarm : Swarm\n The Underworld swarm object.\n mtype : PETSc.DMSwarm.MigrateType\n The migration type to set.\n\n See Also\n --------\n dm_swarm_get_migrate_type : Get the current migration type.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate_velocity", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 211, + "signature": "def evaluate_velocity(self, coords):", + "parameters": [], + "returns": null, + "existing_docstring": "Exact velocity at ``coords`` (N\u00d72 array), via the compiled kernel.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "velocity_error", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 222, + "signature": "def velocity_error(self, velocity_var):", + "parameters": [], + "returns": null, + "existing_docstring": "Relative L2 error of a velocity MeshVariable against the analytic.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate_stress", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 228, + "signature": "def evaluate_stress(self, coords):", + "parameters": [], + "returns": null, + "existing_docstring": "Exact total (Cauchy) stress at ``coords`` (N\u00d72 array), via the\n compiled kernel. Returns an (N, 3) array of (sigma_xx, sigma_yy,\nsigma_xy).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "topography_top", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 245, + "signature": "def topography_top(self, coords):", + "parameters": [], + "returns": null, + "existing_docstring": "Exact dynamic topography -n.sigma.n on the top boundary (n = y_hat),\ni.e. -sigma_yy, at ``coords`` (N\u00d72 array). Returns a length-N array.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "get_structure", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 55, + "signature": "(self, coords: np.ndarray, dofcount: int)", + "parameters": [ + { + "name": "coords", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "dofcount", + "type_hint": "int", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get cached CachedDMInterpolationInfo or None.\n\nParameters\n----------\ncoords : ndarray\n Evaluation coordinates\ndofcount : int\n Total DOF count for current variables\n\nReturns\n-------\ncached_info : CachedDMInterpolationInfo or None\n Cached structure object, or None if not cached or disabled", + "harvested_comments": [ + "Caching disabled", + "Compute cache key", + "Check cache", + "LRU move to end" + ], + "status": "complete", + "needs": [], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "store_structure", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 91, + "signature": "(self, coords: np.ndarray, dofcount: int, cached_info)", + "parameters": [ + { + "name": "coords", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "dofcount", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "cached_info", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Store CachedDMInterpolationInfo in cache.\n\nParameters\n----------\ncoords : ndarray\n Evaluation coordinates\ndofcount : int\n Total DOF count\ncached_info : CachedDMInterpolationInfo\n Cython wrapper object containing DMInterpolation structure", + "harvested_comments": [ + "Don't cache if disabled", + "LRU management: remove oldest entries if we exceed max_entries", + "Remove oldest (first) item", + "Python GC keeps it alive" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "invalidate_all", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 130, + "signature": "(self, reason: str = 'manual')", + "parameters": [ + { + "name": "reason", + "type_hint": "str", + "default": "'manual'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Clear entire cache and destroy all DMInterpolation structures.\n\nIMPORTANT: Must be called from Cython to properly destroy C structures!\nThis method only clears the Python-side tracking.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "invalidate_coords", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 143, + "signature": "(self, coords: np.ndarray)", + "parameters": [ + { + "name": "coords", + "type_hint": "np.ndarray", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Invalidate all cache entries for specific coordinates.", + "harvested_comments": [ + "Remove all entries with this coord hash (different dofcounts)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "get_stats", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 154, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Get cache performance statistics.\n\nReturns\n-------\ndict with metrics:\n - requests: Total get() calls\n - hits: Cache hits\n - misses: Cache misses\n - hit_rate: Percentage of hits\n - entries: Current cache size\n - invalidations: Total invalidation events", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "print_stats", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 179, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Print cache statistics (user-facing).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "reset_stats", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 193, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Reset statistics (but keep cached entries).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DMInterpolationCache", + "is_public": true + }, + { + "name": "simplify_units", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 31, + "signature": "(units)", + "parameters": [ + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Simplify combined units to human-readable form.\n\nConverts complex unit expressions (e.g., megayear * centimeter / year)\nto compact, human-friendly forms (e.g., kilometer) using Pint's\n:meth:`to_compact` method.\n\nParameters\n----------\nunits : pint.Unit or None\n The units to simplify.\n\nReturns\n-------\npint.Unit or None\n Simplified units with appropriate prefixes, or None if input is None.", + "harvested_comments": [ + "Create a quantity with value 1 and these units, then simplify", + "If simplification fails, return original units" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "unwrap_expression", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 201, + "signature": "(expr, mode = 'nondimensional', depth = None)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": "'nondimensional'", + "description": "" + }, + { + "name": "depth", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Unified unwrapping of UW expressions.\n\nThis is the single entry point for all expression unwrapping needs.\n\nArgs:\n expr: Expression to unwrap (UWexpression, SymPy expr, or value)\n mode: 'nondimensional' - for JIT compilation and evaluation (uses .data)\n 'dimensional' - for user display (uses .value)\n 'symbolic' - just expand .sym structure\n 'symbolic_keep_constants' - expand .sym structure but stop at\n truly-constant UWexpressions (keep them as symbols for\n constants[]). Use before Jacobian differentiation.\n depth: Maximum expansion depth (None = complete expansion)\n\nReturns:\n Pure SymPy expression with all UW atoms expanded", + "harvested_comments": [ + "Extract sym if needed", + "Fixed-point iteration (or depth-limited)", + "Safety limit" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "is_constant_expr", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 255, + "signature": "(fn)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Check if expression has no mesh variable dependencies.\n\nAn expression is \"constant\" in the Underworld sense if it doesn't\ndepend on mesh coordinates, mesh variables, or applied functions.\n\nParameters\n----------\nfn : sympy.Expr or UWexpression\n Expression to check.\n\nReturns\n-------\nbool\n True if the expression has no spatial dependencies.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "extract_expressions", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 275, + "signature": "(fn)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Extract all UWexpression atoms from a SymPy expression.\n\nParameters\n----------\nfn : sympy.Expr or UWexpression\n Expression to search.\n\nReturns\n-------\nset\n Set of :class:`UWexpression` objects found in the expression tree.", + "harvested_comments": [ + "exhaustion criterion" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "extract_meshes", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 310, + "signature": "(fn)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Extract all meshes referenced by MeshVariable symbols in an expression.\n\nSearches for UnderworldFunction (applied function) atoms and\ncoordinate BaseScalar atoms, collecting the meshes they belong to.\n\nParameters\n----------\nfn : sympy.Expr, sympy.Matrix, or UWexpression\n Expression to search.\n\nReturns\n-------\nset\n Set of Mesh objects referenced by the expression.", + "harvested_comments": [ + "Check applied functions (e.g., {Tf}(N.x, N.y)) \u2014 the function CLASS", + "carries a weakref to the MeshVariable via 'meshvar'", + "dereference weakref", + "Check coordinate base scalars (N.x, N.y, Gamma.x, etc.)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "extract_expressions_and_functions", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 354, + "signature": "(fn)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Extract all UWexpression, Function, and coordinate atoms.\n\nRecursively searches an expression tree for Underworld-specific\natoms including expressions, applied functions, and coordinate\nbase scalars.\n\nParameters\n----------\nfn : sympy.Expr or UWexpression\n Expression to search.\n\nReturns\n-------\nset\n Set of UWexpression, Function, and BaseScalar objects.", + "harvested_comments": [ + "Handle UWQuantity objects - they don't have atoms() method", + "exhaustion criterion" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "expand", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 430, + "signature": "(expr, depth = None, simplify_result = False)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "depth", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "simplify_result", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Expand UW expression to reveal SymPy structure for inspection.\n\nThis function recursively expands nested UW expressions to reveal their\nunderlying SymPy representation. It's designed for user inspection and\ndebugging - use dimensional values (not scaled).\n\nArgs:\n expr: UW expression to expand\n depth (int, optional): Maximum expansion depth. None = full expansion\n simplify_result (bool): If True, apply SymPy simplification\n\nReturns:\n Pure SymPy expression with all UW wrappers removed (dimensional values)", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "unwrap", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 454, + "signature": "(fn, depth = None, keep_constants = True, return_self = True)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "depth", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keep_constants", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "return_self", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Expand UW expression to reveal SymPy structure.\n\nArgs:\n fn: Expression to unwrap\n depth: Maximum expansion depth (None = complete)\n keep_constants: If False, use nondimensional mode (for JIT)\n return_self: If False, use nondimensional mode (for JIT)\n\nReturns:\n Unwrapped SymPy expression", + "harvested_comments": [ + "JIT compilation path - use nondimensional mode", + "Display path - use dimensional mode" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "unwrap_for_evaluate", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 475, + "signature": "(expr, scaling_active = None)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "scaling_active", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Unwrap expression for evaluate/lambdify path with proper unit handling.\n\nType-based dispatch (2025-12 UWCoordinate design):\n- UWCoordinate: unwrap to BaseScalar (placeholder, NO scaling)\n- UWexpression with units: nondimensionalize via .data\n- UWexpression without units: recursively unwrap .sym\n- UWQuantity: nondimensionalize via .data\n- BaseScalar/MeshVariable.sym: pass through unchanged\n\nReturns:\n tuple: (unwrapped_expr, result_dimensionality)", + "harvested_comments": [ + "Step 1: Get expression dimensionality", + "IMPORTANT: For UWexpression, try the wrapper FIRST because it stores units", + "via ._value_with_units. The raw .sym expression has no unit metadata.", + "Try wrapper first (has unit info from arithmetic)", + "If wrapper has no units, try the .sym expression (may contain unit-aware variables)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "substitute_expr", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 590, + "signature": "(fn, sub_expr, keep_constants = True, return_self = True)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sub_expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "keep_constants", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "return_self", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Substitute a specific expression throughout.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rename", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 753, + "signature": "(self, new_display_name: str) -> 'UWexpression'", + "parameters": [ + { + "name": "new_display_name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "'UWexpression'", + "existing_docstring": "Change the display name of this expression without changing its identity.\n\nThis allows customizing how the expression appears in LaTeX output\nand string representations while preserving its symbolic identity\n(hash and equality remain based on the original name and _uw_id).\n\nParameters\n----------\nnew_display_name : str\n The new name to use for display purposes (typically LaTeX).\n\nReturns\n-------\nUWexpression\n Returns self to allow method chaining.\n\nExamples\n--------\n>>> viscosity = uw.expression(\"eta\", 1e21)\n>>> viscosity.rename(r\"\\eta_{\\mathrm{mantle}}\")\n>>> print(viscosity) # Shows renamed version\n\nNotes\n-----\nThe original symbol name (self.name) is preserved for:\n- SymPy identity (hash, equality)\n- Pickling/serialization\n- Expression matching in solvers\n\nOnly the display representation changes via _latex() and _sympystr().", + "harvested_comments": [ + "Shows renamed version" + ], + "status": "complete", + "needs": [], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "sym", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 891, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the symbolic/numeric value.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "sym", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 896, + "signature": "(self, new_value)", + "parameters": [ + { + "name": "new_value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update the wrapped value.", + "harvested_comments": [ + "TRANSPARENT CONTAINER PRINCIPLE: Store the object directly", + "Keep the full UWQuantity!" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "copy", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 908, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Copy the symbolic value and metadata from another expression.\n\nThis method updates this expression's content while preserving its identity\n(same Python id). Used by constitutive model parameter setters to enable\nlazy evaluation - the expression container stays the same, but its content\ncan be updated.\n\nParameters\n----------\nother : UWexpression or UWQuantity or sympy.Basic or number\n The source to copy from. If UWexpression, copies both ._sym and\n any unit metadata. Otherwise, updates .sym directly.\n\nNotes\n-----\nThis follows the same pattern as ExpressionDescriptor.__set__ in _api_tools.py\nto ensure consistent behavior for expression updates.", + "harvested_comments": [ + "Copy the symbolic value", + "Copy unit metadata for compatibility with older patterns", + "(though Transparent Container principle derives these from _sym)", + "For UWQuantity, numbers, sympy expressions - use .sym setter" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "value", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 944, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the dimensional value of the wrapped thing.", + "harvested_comments": [ + "TRANSPARENT CONTAINER: Always derive from _sym (the wrapped object)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 952, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the non-dimensional value for computation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 970, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get units from the wrapped thing (if it has units).", + "harvested_comments": [ + "TRANSPARENT CONTAINER: Always derive from _sym" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 978, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if the wrapped thing has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 983, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get dimensionality from the wrapped thing.", + "harvested_comments": [ + "TRANSPARENT CONTAINER: Always derive from _sym" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "expression", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 991, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the unwrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "expression_number", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1004, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Unique number of the expression instance.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "quantity", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1013, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the wrapped quantity for numeric arithmetic with units.\n\nReturns the underlying UWQuantity if one was provided, or creates\none from the value.", + "harvested_comments": [ + "TRANSPARENT CONTAINER: Derive from _sym (the wrapped object)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "to", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1030, + "signature": "(self, target_units: str) -> 'UWexpression'", + "parameters": [ + { + "name": "target_units", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "'UWexpression'", + "existing_docstring": "Convert to different units.\n\nDelegates to uw.convert_units() for the actual conversion.\n\nParameters\n----------\ntarget_units : str\n Target units (e.g., \"m/s\", \"km\", \"degC\")\n\nReturns\n-------\nUWexpression\n New expression with converted value and units\n\nExamples\n--------\n>>> radius = uw.expression(\"r\", uw.quantity(6370, \"km\"))\n>>> radius_m = radius.to(\"m\")\n>>> print(radius_m.value) # 6370000.0", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "to_base_units", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1055, + "signature": "(self) -> 'UWexpression'", + "parameters": [], + "returns": "'UWexpression'", + "existing_docstring": "Convert to SI base units.\n\nDelegates to uw.to_base_units() for the actual conversion.\n\nReturns\n-------\nUWexpression\n New expression with value in SI base units\n\nExamples\n--------\n>>> velocity = uw.expression(\"v\", uw.quantity(100, \"km/h\"))\n>>> velocity_si = velocity.to_base_units()\n>>> print(velocity_si.value) # 27.78 (m/s)", + "harvested_comments": [ + "27.78 (m/s)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "to_reduced_units", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1075, + "signature": "(self) -> 'UWexpression'", + "parameters": [], + "returns": "'UWexpression'", + "existing_docstring": "Simplify units by canceling common factors.\n\nDelegates to uw.to_reduced_units() for the actual simplification.\n\nReturns\n-------\nUWexpression\n New expression with simplified units", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "to_compact", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1089, + "signature": "(self) -> 'UWexpression'", + "parameters": [], + "returns": "'UWexpression'", + "existing_docstring": "Convert to most human-readable unit representation.\n\nAutomatically chooses unit prefixes (kilo, mega, micro, etc.)\nto make the number more readable.\n\nDelegates to uw.to_compact() for the actual conversion.\n\nReturns\n-------\nUWexpression\n New expression with compact units\n\nExamples\n--------\n>>> length = uw.expression(\"L\", uw.quantity(0.001, \"km\"))\n>>> length_compact = length.to_compact()\n>>> print(length_compact) # 1.0 [meter]", + "harvested_comments": [ + "1.0 [meter]" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "atoms", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1124, + "signature": "(self, *types)", + "parameters": [ + { + "name": "*types", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Use Symbol's atoms() method.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_number", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1145, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "UWexpression is a Symbol, not a number.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_comparable", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1150, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate to wrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_extended_real", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1157, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate to wrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_positive", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1164, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate to wrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_negative", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1171, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate to wrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_zero", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1178, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate to wrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_finite", + "kind": "property", + "file": "src/underworld3/function/expressions.py", + "line": 1185, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate to wrapped expression.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_constant", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1191, + "signature": "(self, *wrt, **flags)", + "parameters": [ + { + "name": "*wrt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**flags", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "SymPy-compatible is_constant - delegate to Symbol.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "is_uw_constant", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1195, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "UW-specific: does this have no mesh variable dependencies?", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "constant", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1199, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated - use is_uw_constant().", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "diff", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1203, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Differentiation - delegate to Symbol.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": true + }, + { + "name": "doit", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1773, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Evaluate the derivative.\n\nReturns\n-------\nsympy.Basic\n The result of differentiating the expression.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWDerivativeExpression", + "is_public": true + }, + { + "name": "mesh_vars_in_expression", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 1791, + "signature": "(expr)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Find all mesh variables and derivatives in an expression.\n\nTraverses the expression tree to find:\n- Regular mesh variable functions (UnderworldAppliedFunction)\n- Derivative functions (UnderworldAppliedFunctionDeriv), grouped by source variable\n\nReturns:\n tuple: (mesh, regular_vars, derivative_vars)\n - mesh: Common mesh for all variables (None if no mesh vars)\n - regular_vars: set of UnderworldAppliedFunction objects\n - derivative_vars: dict mapping source MeshVariable -> list of (deriv_expr, diffindex)\n where diffindex is 0=x, 1=y, 2=z derivative direction", + "harvested_comments": [ + "source_meshvar -> [(deriv_expr, diffindex), ...]", + "Check for derivative functions - collect instead of raising error", + "Don't recurse into derivative args - they are just coordinates", + "Check derivatives FIRST - they inherit from UnderworldAppliedFunction", + "so must be checked before the parent class" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "project_to_degree", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 26, + "signature": "(mesh_var: 'MeshVariable', target_degree: int = 1, continuous: bool = True, include_ghosts: bool = True) -> np.ndarray", + "parameters": [ + { + "name": "mesh_var", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "target_degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "continuous", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "include_ghosts", + "type_hint": "bool", + "default": "True", + "description": "" + } + ], + "returns": "np.ndarray", + "existing_docstring": "Project a MeshVariable to a different polynomial degree.\n\nParameters\n----------\nmesh_var\n Source MeshVariable (any degree, scalar/vector/tensor).\ntarget_degree\n Polynomial degree of the target space (default 1 = vertex values).\ncontinuous\n Whether the target space is continuous (default ``True``).\ninclude_ghosts\n If ``True`` (default), return the full local vector including ghost\n DOFs. If ``False``, return only the owned partition (suitable for\n parallel HDF5 writing where each rank writes its own slice).\n\nReturns\n-------\nnp.ndarray\n Projected values with shape ``(n_dofs, num_components)``.\n\nNotes\n-----\nThis creates a transient scratch DM, builds the PETSc interpolation\nmatrix, applies it, and destroys everything. The mesh DM and all\nexisting MeshVariables are completely untouched.\n\nFor ``target_degree == mesh_var.degree`` the interpolation matrix is\nthe identity and the result matches the source data exactly.", + "harvested_comments": [ + "PETSc: source_subdm.createInterpolation(target_dm) returns a matrix", + "whose mult() maps source global vec \u2192 target global vec." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "project_to_vertices", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 112, + "signature": "(mesh_var: 'MeshVariable') -> np.ndarray", + "parameters": [ + { + "name": "mesh_var", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + } + ], + "returns": "np.ndarray", + "existing_docstring": "Shorthand: project any MeshVariable to P1 (vertex) values.\n\nParameters\n----------\nmesh_var\n Source MeshVariable.\n\nReturns\n-------\nnp.ndarray\n Values at mesh vertices, shape ``(n_vertices, num_components)``.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "write_vertices_to_viewer", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 229, + "signature": "(mesh_var: 'MeshVariable', viewer: 'PETSc.ViewerHDF5', group: str = '/vertex_fields', name: str | None = None) -> None", + "parameters": [ + { + "name": "mesh_var", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "viewer", + "type_hint": "'PETSc.ViewerHDF5'", + "default": null, + "description": "" + }, + { + "name": "group", + "type_hint": "str", + "default": "'/vertex_fields'", + "description": "" + }, + { + "name": "name", + "type_hint": "str | None", + "default": "None", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Project a MeshVariable to P1 vertex values and write via PETSc ViewerHDF5.\n\nFor P1 continuous variables, the existing global vector data is\nwritten directly (DOFs already match mesh vertices). For P2+\ncontinuous variables, a scratch DM and PETSc interpolation matrix\nproject to degree 1.\n\nTensor variables (TENSOR, SYM_TENSOR) are repacked from UW3's\ninternal ``_data_layout`` ordering to ParaView's 9-component\nrow-major 3x3 format. The checkpoint data in ``/fields/`` is\nunchanged \u2014 only the visualisation copy is repacked.\n\nData is written as a standalone Vec (no DM) so that ``pushGroup``\nis respected \u2014 DM-associated Vecs would be redirected to ``/fields/``\nby the DMPlex HDF5 writer.\n\nThe vector is written under *group* (default ``/vertex_fields``)\nwith the dataset name *name* (default ``_``\nto match the existing XDMF convention).\n\nParameters\n----------\nmesh_var\n Source MeshVariable (any degree, scalar/vector/tensor).\nviewer\n An open ``PETSc.ViewerHDF5`` in append or write mode.\ngroup\n HDF5 group path to write into.\nname\n Dataset name. Defaults to ``_``.", + "harvested_comments": [ + "DOFs = vertices \u2014 use gvec data directly (already owned-only)", + "include_ghosts=False \u2192 owned partition only, suitable for", + "parallel HDF5 writing where each rank writes its own slice.", + "Repack tensors to ParaView 9-component format for visualisation.", + "The projected data uses the same _data_layout as the source variable." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "write_coordinates_to_viewer", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 299, + "signature": "(mesh, viewer: 'PETSc.ViewerHDF5', group: str = '/vertex_fields', name: str = 'coordinates') -> None", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "viewer", + "type_hint": "'PETSc.ViewerHDF5'", + "default": null, + "description": "" + }, + { + "name": "group", + "type_hint": "str", + "default": "'/vertex_fields'", + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "'coordinates'", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Write mesh vertex coordinates via PETSc ViewerHDF5.\n\nParameters\n----------\nmesh\n Source mesh.\nviewer\n An open ``PETSc.ViewerHDF5`` in append or write mode.\ngroup\n HDF5 group path to write into.\nname\n Dataset name (default ``coordinates``).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "write_cell_field_to_viewer", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 323, + "signature": "(mesh_var: 'MeshVariable', viewer: 'PETSc.ViewerHDF5', group: str = '/cell_fields', name: str | None = None) -> None", + "parameters": [ + { + "name": "mesh_var", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "viewer", + "type_hint": "'PETSc.ViewerHDF5'", + "default": null, + "description": "" + }, + { + "name": "group", + "type_hint": "str", + "default": "'/cell_fields'", + "description": "" + }, + { + "name": "name", + "type_hint": "str | None", + "default": "None", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Write a cell (discontinuous/DG-0) variable via PETSc ViewerHDF5.\n\nData is written as a standalone Vec (no DM) so that ``pushGroup``\nis respected.\n\nParameters\n----------\nmesh_var\n Source MeshVariable (discontinuous or degree 0).\nviewer\n An open ``PETSc.ViewerHDF5`` in append or write mode.\ngroup\n HDF5 group path to write into.\nname\n Dataset name. Defaults to ``_``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 825, + "signature": "(expr, coords, coord_sys = None, other_arguments = None, simplify = False, verbose = False, evalf = False, mode = 'default', data_layout = None, check_extrapolated = False, smoothing = 1e-06, rbf = None, force_l2 = None, monotone = False)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coord_sys", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "other_arguments", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "simplify", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "evalf", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": "'default'", + "description": "" + }, + { + "name": "data_layout", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "check_extrapolated", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "smoothing", + "type_hint": null, + "default": "1e-06", + "description": "" + }, + { + "name": "rbf", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "force_l2", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "monotone", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Evaluate ``expr`` at ``coords`` with automatic unit handling.\n\nThin wrapper over :func:`_evaluate_impl`. See that function for the\nfull parameter documentation and evaluation-mode notes. With the\ndefault ``monotone=False`` this is bit-identical to the historical\n``evaluate``.\n\nCoordinates are a plain ``(N, dim)`` array in model (non-dimensional)\nunits, or a ``UnitAwareArray``/``UWQuantity`` array which is\nnon-dimensionalised automatically. Python lists/tuples of individual\nquantity objects (``[(x_qty, y_qty)]``) are NOT supported \u2014 convert\nphysical locations with :func:`underworld3.scaling.non_dimensionalise`\nand pass a numpy array (units-family ruling, 2026-07).\n\nParameters\n----------\nmonotone : bool or str, optional\n Opt-in bounded (monotone) interpolation, applied as a\n post-process to the computed result. ``False`` (default) \u2192 no\n limiting. ``True`` / ``\"clamp\"`` \u2192 clip the result into the\n ``[min, max]`` of the ``mesh.dim + 1`` nearest source-field DOFs.\n ``\"pick\"`` \u2192 keep in-bounds values and re-evaluate only the\n out-of-bounds subset via (bounded) RBF interpolation. Only\n single-MeshVariable expressions are supported; composites raise\n ``ValueError``. See :func:`_apply_monotone_limit`.", + "harvested_comments": [ + "Expert overrides (override mode settings)", + "Validate up front so an unknown option fails fast (no wasted eval)." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "global_evaluate", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 903, + "signature": "(expr, coords = None, coord_sys = None, other_arguments = None, simplify = False, verbose = False, evalf = False, mode = 'default', data_layout = None, check_extrapolated = False, smoothing = 1e-06, rbf = None, force_l2 = None, monotone = False, local_fallback = True)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "coord_sys", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "other_arguments", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "simplify", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "evalf", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": "'default'", + "description": "" + }, + { + "name": "data_layout", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "check_extrapolated", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "smoothing", + "type_hint": null, + "default": "1e-06", + "description": "" + }, + { + "name": "rbf", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "force_l2", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "monotone", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "local_fallback", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Parallel-safe evaluate with automatic unit-aware results.\n\nThin wrapper over :func:`_global_evaluate_impl`. See that function and\n:func:`evaluate` for the full parameter documentation. With the\ndefault ``monotone=False`` this is bit-identical to the historical\n``global_evaluate``.\n\nParameters\n----------\nmonotone : bool or str, optional\n Opt-in bounded (monotone) interpolation post-process. See\n :func:`evaluate` for semantics. Not supported together with\n ``check_extrapolated`` (raises ``NotImplementedError``).\nlocal_fallback : bool, optional\n Parallel-only best-claim resolution of out-of-domain points so the\n result matches serial ``evaluate``. Default True. See\n :func:`_global_evaluate_impl`.", + "harvested_comments": [ + "Expert overrides (override mode settings)", + "Validate up front so an unknown option or the unsupported", + "monotone + check_extrapolated combination fails fast (no wasted eval)." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate_gradient", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 48, + "signature": "(scalar_var, coords, method = 'interpolant', component = None)", + "parameters": [ + { + "name": "scalar_var", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "method", + "type_hint": null, + "default": "'interpolant'", + "description": "" + }, + { + "name": "component", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Evaluate gradient of a mesh variable at arbitrary coordinates.\n\nComputes the gradient of a MeshVariable (or one of its components) and\nevaluates it at the specified coordinates.\n\nParameters\n----------\nscalar_var : MeshVariable\n Field to compute gradient of. Can be scalar (num_components=1) or\n vector/tensor field. For multi-component fields, use `component`\n parameter to specify which component's gradient to compute.\ncoords : array-like\n Coordinates at which to evaluate gradient, shape (n_points, dim).\n Can be numpy array or UnitAwareArray.\nmethod : str, optional\n Gradient computation method:\n - \"interpolant\": Clement interpolant (O(h) accurate, no solve). Default.\n - \"projection\": L2 projection (O(h\u00b2) accurate, requires solve).\ncomponent : int or None, optional\n For multi-component fields, which component to compute gradient of.\n If None and field has multiple components, raises ValueError.\n For scalar fields, this parameter is ignored.\n\nReturns\n-------\nndarray\n Gradient values at requested coordinates, shape (n_points, dim).\n gradient[i, j] = \u2202f/\u2202x\u2c7c at coords[i].\n\nNotes\n-----\n**Interpolant (Clement) Method**: Uses PETSc's\n`DMPlexComputeGradientClementInterpolant` which averages cell-wise gradients\nat vertices. This is O(h) accurate - error halves when mesh resolution doubles.\nFast but limited to first-order accuracy.\n\n**Projection (L2) Method**: Solves a mass matrix system to find the optimal\nL2 projection of the gradient onto the finite element space. This is O(h\u00b2)\naccurate for smooth solutions. The projection is cached on the mesh for\nrepeated calls, using the previous solution as initial guess.\n\nExamples\n--------\n>>> mesh = uw.meshing.StructuredQuadBox(elementRes=(16, 16))\n>>> T = uw.discretisation.MeshVariable('T', mesh, 1)\n>>> T.array[:, 0, 0] = T.coords[:, 0]**2 # T = x\u00b2\n>>>\n>>> # Fast gradient (O(h))\n>>> grad_fast = evaluate_gradient(T, coords, method=\"interpolant\")\n>>>\n>>> # Accurate gradient (O(h\u00b2))\n>>> grad_accurate = evaluate_gradient(T, coords, method=\"projection\")\n\nSee Also\n--------\nuw.systems.Projection : Direct L2 projection for explicit control\nuw.function.evaluate : General function evaluation\n\nReferences\n----------\n.. [1] Cl\u00e9ment, P. (1975). \"Approximation by finite element functions\n using local regularization\". RAIRO Analyse num\u00e9rique, 9(R-2), 77-84.", + "harvested_comments": [ + "Fast gradient (O(h))", + "Accurate gradient (O(h\u00b2))" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "interpolate_gradients_at_coords", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 493, + "signature": "(source_vars, coords, mesh, method = 'interpolant')", + "parameters": [ + { + "name": "source_vars", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "method", + "type_hint": null, + "default": "'interpolant'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute gradients for multiple source variables and interpolate.\n\nComputes gradients for each source variable using the specified method\nand evaluates at the specified coordinates.\n\nParameters\n----------\nsource_vars : list of MeshVariable or list of (MeshVariable, int) tuples\n Fields needing gradient computation. For scalar fields, just pass\n the MeshVariable. For multi-component fields, pass tuples of\n (MeshVariable, component_index).\ncoords : array-like\n Coordinates at which to evaluate gradients, shape (n_points, dim).\nmesh : Mesh\n The mesh containing the variables.\nmethod : str, optional\n Gradient computation method:\n - \"interpolant\": Clement interpolant (O(h) accurate, no solve). Default.\n - \"projection\": L2 projection (O(h\u00b2) accurate, requires solve).\n\nReturns\n-------\ndict\n Mapping from (var, component) tuple to gradient array (n_points, dim).\n For scalar fields, component is 0.\n result[(var, component)][i, j] = \u2202var[component]/\u2202x\u2c7c at coords[i].\n\nNotes\n-----\nFor an expression with multiple derivatives like `T.diff(x) + v[0].diff(y)`:\n- Identifies source variables and components: {(T, 0), (v, 0)}\n- Computes gradient once per (variable, component) pair\n- Each derivative component extracted from the appropriate gradient\n\nThis is called internally by evaluate_nd when derivatives are detected.", + "harvested_comments": [ + "Normalize input: convert bare variables to (var, component) tuples", + "Bare variable - assume scalar (component 0)", + "For multi-component fields, caller should specify components", + "Multi-component field passed without component spec", + "Compute gradient for all components" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "compute_clement_gradient_at_nodes", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 570, + "signature": "(var, component = None)", + "parameters": [ + { + "name": "var", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "component", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute Clement gradient at mesh nodes only (no interpolation).\n\nThis is the raw Clement interpolant without arbitrary point evaluation.\nUseful when you only need values at mesh nodes, e.g., for error estimation\nor visualization at node locations.\n\nParameters\n----------\nvar : MeshVariable\n Field to compute gradient of. Can be scalar or multi-component.\ncomponent : int or None, optional\n For multi-component fields, which component to compute gradient of.\n If None and field is scalar, uses the only component.\n If None and field is multi-component, raises ValueError.\n\nReturns\n-------\nndarray\n Gradient at mesh nodes, shape (n_nodes, dim).\n gradient[i, j] = \u2202f/\u2202x\u2c7c at node i.\n\nNotes\n-----\nUses PETSc's `DMPlexComputeGradientClementInterpolant` which computes\nthe L2 projection of cell-wise constant gradients onto a continuous\nP1 (linear) space by averaging at shared vertices.\n\nThis is O(h) accurate - suitable for error estimation, quick visualization,\nor when higher accuracy is not required.\n\nFor higher-degree fields (P2, etc.), the function first samples the field\nat P1 vertex locations before computing the Clement gradient.\n\nExamples\n--------\n>>> T.array[:, 0, 0] = T.coords[:, 0]**2 + T.coords[:, 1]**2\n>>> grad = compute_clement_gradient_at_nodes(T)\n>>> # grad[i] \u2248 [2*x_i, 2*y_i] at each node\n\n>>> # For vector field, specify component\n>>> grad_vx = compute_clement_gradient_at_nodes(v, component=0)", + "harvested_comments": [ + "grad[i] \u2248 [2*x_i, 2*y_i] at each node", + "For vector field, specify component", + "Handle multi-component fields", + "Get vertex coordinates", + "Get field values at P1 vertex locations" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "is_pure_sympy_expression", + "kind": "function", + "file": "src/underworld3/function/pure_sympy_evaluator.py", + "line": 22, + "signature": "(expr)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Detect if an expression contains only pure sympy symbols or mesh coordinates (no UW3 variable data).\n\nExpressions are considered \"pure\" (lambdifiable) if they contain:\n- Only sympy.Symbol objects\n- Only mesh coordinate BaseScalars (mesh.X[0], mesh.X[1], etc.)\n- Mix of Symbols and BaseScalars\n- No UW3 MeshVariable or SwarmVariable data\n\nParameters\n----------\nexpr : sympy expression\n Expression to check\n\nReturns\n-------\nbool\n True if expression can be lambdified without mesh data interpolation\nsymbols : set or None\n Set of free symbols if pure, None otherwise\nsymbol_type : str or None\n 'symbol', 'coordinate', or 'mixed' indicating what symbols were found\n\nExamples\n--------\n>>> import sympy\n>>> x = sympy.Symbol('x')\n>>> t = sympy.Symbol('t')\n>>> is_pure_sympy_expression(x**2 + t)\n(True, {x, t}, 'symbol')\n\n>>> # With mesh coordinates\n>>> x_mesh = mesh.X[0]\n>>> is_pure_sympy_expression(sympy.erf(x_mesh - 0.5))\n(True, {N.x}, 'coordinate')\n\n>>> # With UW3 variable data - NOT pure\n>>> T = uw.discretisation.MeshVariable(\"T\", mesh, 1)\n>>> is_pure_sympy_expression(T.sym[0] + x)\n(False, None, None)", + "harvested_comments": [ + "With mesh coordinates", + "With UW3 variable data - NOT pure", + "Get all free symbols", + "Not a sympy expression", + "Constant expression - can be handled efficiently" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_cached_lambdified", + "kind": "function", + "file": "src/underworld3/function/pure_sympy_evaluator.py", + "line": 158, + "signature": "(expr, symbols, modules = ('scipy', 'numpy'))", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "symbols", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "modules", + "type_hint": null, + "default": "('scipy', 'numpy')", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get a cached lambdified function for an expression.\n\nUses an LRU cache to avoid recompiling the same expression multiple times.\n\nParameters\n----------\nexpr : sympy expression\n Expression to lambdify\nsymbols : tuple of sympy.Symbol\n Symbols in order for lambdify\nmodules : tuple of str, optional\n Modules to use for lambdify. Default: ('scipy', 'numpy')\n scipy is required for special functions like erf, gamma, etc.\n\nReturns\n-------\ncallable\n Lambdified function\n\nNotes\n-----\nCache key is based on:\n- Expression structure (hash of srepr)\n- Symbol names and order\n- Modules used", + "harvested_comments": [ + "Create cache key", + "Check cache", + "Lambdify with scipy for special functions", + "Fallback to numpy only if scipy fails" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "evaluate_pure_sympy", + "kind": "function", + "file": "src/underworld3/function/pure_sympy_evaluator.py", + "line": 212, + "signature": "(expr, coords, coord_symbols = None)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coord_symbols", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Fast evaluation of pure sympy expressions using cached lambdified functions.\n\nThis function provides optimized evaluation for expressions containing only\npure sympy symbols (no UW3 MeshVariable symbols). It automatically:\n1. Detects the free symbols in the expression\n2. Maps coordinate columns to symbols\n3. Uses cached lambdified functions for efficiency\n\nParameters\n----------\nexpr : sympy expression\n Pure sympy expression to evaluate\ncoords : np.ndarray\n Coordinates at which to evaluate, shape (n_points, n_dims)\n For 2D: coords[:, 0] are x values, coords[:, 1] are y values\n For 3D: coords[:, 0] are x, coords[:, 1] are y, coords[:, 2] are z\ncoord_symbols : tuple of sympy.Symbol, optional\n Symbols representing coordinates in order (x, y, z)\n If None, will try to infer from expression's free symbols\n\nReturns\n-------\nnp.ndarray\n Evaluated expression values, shape depends on expression:\n - Scalar expr: (n_points,) or (n_points, 1, 1)\n - Vector expr: (n_points, n_components)\n - Matrix expr: (n_points, n_rows, n_cols)\n\nExamples\n--------\n>>> import sympy\n>>> import numpy as np\n>>> x, y = sympy.symbols('x y')\n>>> expr = sympy.sqrt(x**2 + y**2) # Distance from origin\n>>> coords = np.array([[1.0, 0.0], [0.0, 1.0], [3.0, 4.0]])\n>>> result = evaluate_pure_sympy(expr, coords, coord_symbols=(x, y))\n>>> # result = [1.0, 1.0, 5.0]\n\nNotes\n-----\n- Uses scipy for special functions (erf, gamma, etc.)\n- Caches lambdified functions for repeated evaluations\n- ~10,000x faster than sympy.subs() for many points", + "harvested_comments": [ + "Distance from origin", + "result = [1.0, 1.0, 5.0]", + "Ensure coords is 2D numpy array", + "Handle Matrix expressions", + "For matrices, we'll evaluate element-wise and reshape" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "clear_lambdify_cache", + "kind": "function", + "file": "src/underworld3/function/pure_sympy_evaluator.py", + "line": 441, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Clear the cached lambdified functions.\n\nUseful for testing or if memory usage becomes a concern.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "value", + "kind": "property", + "file": "src/underworld3/function/quantities.py", + "line": 171, + "signature": "(self) -> Union[float, np.ndarray]", + "parameters": [], + "returns": "Union[float, np.ndarray]", + "existing_docstring": "Dimensional value (what the user sees).\n\nReturns\n-------\nfloat or np.ndarray\n The value in the quantity's units", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/function/quantities.py", + "line": 183, + "signature": "(self) -> Union[float, np.ndarray]", + "parameters": [], + "returns": "Union[float, np.ndarray]", + "existing_docstring": "Non-dimensional value (what the solver sees).\n\nReturns the value scaled by the model's reference quantities.\nIf no model is registered or no scaling is active, returns the\ndimensional value.\n\nReturns\n-------\nfloat or np.ndarray\n Non-dimensional value for solver use", + "harvested_comments": [ + "Compute non-dimensional value" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "magnitude", + "kind": "property", + "file": "src/underworld3/function/quantities.py", + "line": 236, + "signature": "(self) -> Union[float, np.ndarray]", + "parameters": [], + "returns": "Union[float, np.ndarray]", + "existing_docstring": "Alias for .value (Pint compatibility).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/function/quantities.py", + "line": 241, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the Pint Unit object.\n\nReturns\n-------\npint.Unit or None\n The unit, or None if dimensionless", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/function/quantities.py", + "line": 253, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Check if this quantity has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/function/quantities.py", + "line": 258, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Get the Pint dimensionality dictionary.\n\nReturns\n-------\ndict\n e.g., {'[length]': 1, '[time]': -1} for velocity", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "to", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 275, + "signature": "(self, target_units: str) -> 'UWQuantity'", + "parameters": [ + { + "name": "target_units", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "'UWQuantity'", + "existing_docstring": "Convert to different units.\n\nParameters\n----------\ntarget_units : str\n Target units (e.g., \"m/s\", \"km\", \"degC\")\n\nReturns\n-------\nUWQuantity\n New quantity with converted value and units", + "harvested_comments": [ + "Output boundary: a .to(\"degC\")/.to(\"degF\") display result is allowed", + "to keep its offset unit \u2014 do NOT re-normalise it back to kelvin", + "(normalisation is an input-only concern). For all other targets this", + "is a no-op (the unit is already multiplicative)." + ], + "status": "complete", + "needs": [], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "to_base_units", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 301, + "signature": "(self) -> 'UWQuantity'", + "parameters": [], + "returns": "'UWQuantity'", + "existing_docstring": "Convert to SI base units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "to_reduced_units", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 309, + "signature": "(self) -> 'UWQuantity'", + "parameters": [], + "returns": "'UWQuantity'", + "existing_docstring": "Simplify units by canceling common factors.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "to_compact", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 317, + "signature": "(self) -> 'UWQuantity'", + "parameters": [], + "returns": "'UWQuantity'", + "existing_docstring": "Convert to most readable unit representation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "diff", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 800, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Derivative of a constant is zero.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": true + }, + { + "name": "quantity", + "kind": "function", + "file": "src/underworld3/function/quantities.py", + "line": 883, + "signature": "(value: Union[float, int, np.ndarray], units: Optional[str] = None) -> UWQuantity", + "parameters": [ + { + "name": "value", + "type_hint": "Union[float, int, np.ndarray]", + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + } + ], + "returns": "UWQuantity", + "existing_docstring": "Create a unit-aware quantity.\n\nParameters\n----------\nvalue : float, int, or array-like\n The numerical value\nunits : str, optional\n Units specification (e.g., \"Pa*s\", \"cm/year\", \"K\")\n\nReturns\n-------\nUWQuantity\n Unit-aware quantity\n\nExamples\n--------\n>>> viscosity = uw.quantity(1e21, \"Pa*s\")\n>>> velocity = uw.quantity(5, \"cm/year\")\n>>> dT = uw.quantity(1000, \"K\") - uw.quantity(273, \"K\")", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "has_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 95, + "signature": "(obj)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Check if an object has unit information.\n\nParameters\n----------\nobj : any\n Object to check for unit information\n\nReturns\n-------\nbool\n True if object has detectable units", + "harvested_comments": [ + "Check for UWQuantity", + "Check for Pint quantity", + "Check for array with unit metadata", + "Check for NDArray with unit information" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 128, + "signature": "(obj)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Extract unit information from an object.\n\nDelegates to ``underworld3.units.get_units`` which is the canonical\nimplementation. This wrapper exists because compiled Cython modules\n(e.g. ``ckdtree.pyx``) import from this location.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "compute_expression_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 140, + "signature": "(expr)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute units for compound SymPy expressions using dimensional analysis.\n\nThis function traverses the expression tree and uses Pint to perform\ndimensional arithmetic on the units of sub-expressions.\n\nParameters\n----------\nexpr : sympy expression\n Expression to analyze (e.g., temperature / y)\n\nReturns\n-------\npint.Unit or None\n Computed unit object, or None if cannot determine\n\nExamples\n--------\n>>> # T.sym has units 'kelvin', y has units 'kilometer'\n>>> compute_expression_units(T.sym / y)\n\n\n>>> # velocity has units 'm/s', time has units 's'\n>>> compute_expression_units(velocity * time)\n\n\n>>> # Derivative: dT/dx\n>>> compute_expression_units(T.sym.diff(mesh.N.x))\n\n\nChanged in 2025-10-16: Now returns pint.Unit objects instead of strings.", + "harvested_comments": [ + "T.sym has units 'kelvin', y has units 'kilometer'", + "velocity has units 'm/s', time has units 's'", + "Derivative: dT/dx", + "Helper to check if a pint.Unit is dimensionless", + "Priority -1: Check for DERIVATIVES first (before general UnderworldFunction)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_mesh_coordinate_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 386, + "signature": "(mesh_or_expr)", + "parameters": [ + { + "name": "mesh_or_expr", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get the coordinate units expected by a mesh or expression.\n\nParameters\n----------\nmesh_or_expr : Mesh or sympy expression\n Mesh object or expression containing mesh variables\n\nReturns\n-------\ndict or None\n Dictionary with coordinate unit information, or None if not available", + "harvested_comments": [ + "Try to extract mesh from expression", + "Get coordinate system information", + "Check if mesh has coordinate scaling (units applied)", + "Return unit information - internal units are what the mesh expects", + "Mesh expects internal model units" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "convert_coordinates_to_mesh_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 428, + "signature": "(coords, mesh_info, coord_units = None)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh_info", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coord_units", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert coordinate array to mesh unit system with explicit unit specification.\n\nFollowing UW3 policy: no implicit unit conversions. Coordinates must have\nexplicit units or are assumed to be in model units.\n\nParameters\n----------\ncoords : array-like\n Coordinate array\nmesh_info : dict\n Mesh coordinate unit information from get_mesh_coordinate_units()\ncoord_units : str, optional\n Explicit coordinate units. If None, assumes model coordinates.\n\nReturns\n-------\nnumpy.ndarray\n Coordinates converted to mesh unit system\n\nRaises\n------\nValueError\n If coordinate units are specified but mesh has no scaling context", + "harvested_comments": [ + "Extract coordinate values and ensure float64", + "If no mesh info or mesh is not scaled, coordinates must be model units", + "For scaled meshes with explicit coordinate units", + "Convert physical coordinates to model coordinates", + "Create a temporary quantity for proper unit conversion" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "detect_coordinate_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 501, + "signature": "(coords)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Detect what unit system coordinates are in.\n\nParameters\n----------\ncoords : array-like\n Coordinate array\n\nReturns\n-------\ndict\n Information about coordinate units", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "add_expression_units_to_result", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 522, + "signature": "(result, expression, mesh_info)", + "parameters": [ + { + "name": "result", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh_info", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add appropriate units to evaluation result based on expression analysis.\n\nAnalyzes the expression to determine its physical units and converts\nthe model-unit result back to appropriate physical units.\n\nParameters\n----------\nresult : numpy.ndarray\n Raw evaluation result in model units from PETSc\nexpression : sympy expression\n Expression that was evaluated\nmesh_info : dict\n Mesh coordinate unit information\n\nReturns\n-------\narray or UWQuantity\n Result with appropriate units if detectable, otherwise plain array", + "harvested_comments": [ + "Try to determine the units of the expression", + "Convert result from model units to physical units", + "No units detectable - return plain array (likely dimensionless)", + "If unit analysis fails, return plain result" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "determine_expression_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 561, + "signature": "(expression, mesh_info)", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh_info", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Determine the physical units of a SymPy expression.\n\nAnalyzes the expression to infer what units the result should have\nbased on the constituent variables and operations. This now uses\ndimensional arithmetic for compound expressions.\n\nParameters\n----------\nexpression : sympy expression\n Expression to analyze\nmesh_info : dict\n Mesh coordinate unit information (optional, can be None)\n\nReturns\n-------\nstr or None\n Unit string if determinable, None if dimensionless or unknown\n\nNotes\n-----\nThis function now delegates to compute_expression_units() which performs\ndimensional arithmetic using Pint. This ensures consistent behavior between\nget_units() and determine_expression_units().", + "harvested_comments": [ + "Use the unified dimensional analysis function", + "If analysis fails, assume dimensionless" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "add_units_to_result", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 595, + "signature": "(result, expression)", + "parameters": [ + { + "name": "result", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add appropriate units to evaluation result based on expression.\n\nParameters\n----------\nresult : numpy.ndarray\n Raw evaluation result\nexpression : sympy expression\n Expression that was evaluated\n\nReturns\n-------\narray or UWQuantity\n Result with appropriate units if detectable", + "harvested_comments": [ + "This is the old function - kept for backward compatibility", + "New function is add_expression_units_to_result" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "convert_quantity_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 616, + "signature": "(quantity, target_units)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "target_units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert UWQuantity or Pint quantity to target units.\n\nParameters\n----------\nquantity : UWQuantity, Pint quantity, or array-like\n The quantity to convert\ntarget_units : str or Pint unit\n Target units to convert to\n\nReturns\n-------\nconverted quantity\n Quantity converted to target units", + "harvested_comments": [ + "Handle UWQuantity", + "Convert using Pint", + "Return new UWQuantity", + "Handle direct Pint quantity", + "Handle plain arrays - assume they're already in target units" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "detect_quantity_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 650, + "signature": "(obj)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Detect units of any object (UWQuantity, Pint, array with metadata).\n\nParameters\n----------\nobj : any\n Object to detect units from\n\nReturns\n-------\ndict\n Dictionary with unit information:\n - 'has_units': bool\n - 'units': str or None\n - 'is_dimensionless': bool\n - 'unit_type': str ('UWQuantity', 'Pint', 'metadata', 'none')", + "harvested_comments": [ + "Check for UWQuantity", + "Check for Pint quantity", + "Check for array with unit metadata", + "Check for NDArray with unit information", + "No units detected" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "make_dimensionless", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 713, + "signature": "(quantity, reference_scales)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "reference_scales", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert physical quantity to dimensionless using reference scales.\n\nParameters\n----------\nquantity : UWQuantity or Pint quantity\n Physical quantity to make dimensionless\nreference_scales : dict or Model\n Dictionary of reference scales or Model with reference quantities\n\nReturns\n-------\nUWQuantity\n Dimensionless quantity", + "harvested_comments": [ + "Handle Model object", + "Get quantity info", + "Already dimensionless", + "Extract Pint quantity", + "Determine appropriate scale based on quantity dimensions" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "convert_array_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 802, + "signature": "(array, from_units, to_units)", + "parameters": [ + { + "name": "array", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "from_units", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "to_units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert array from one unit system to another.\n\nParameters\n----------\narray : array-like\n Array values to convert\nfrom_units : str or Pint unit\n Source units\nto_units : str or Pint unit\n Target units\n\nReturns\n-------\nnumpy.ndarray\n Converted array values", + "harvested_comments": [ + "Create a temporary quantity for conversion", + "Extract the magnitude" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "auto_convert_to_mesh_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 835, + "signature": "(array, mesh)", + "parameters": [ + { + "name": "array", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert array coordinates to mesh unit system.\n\nParameters\n----------\narray : array-like\n Coordinate array that may have units\nmesh : Mesh\n Mesh to get unit system from\n\nReturns\n-------\nnumpy.ndarray\n Coordinates converted to mesh unit system", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "convert_evaluation_result", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 855, + "signature": "(result, target_units)", + "parameters": [ + { + "name": "result", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "target_units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert evaluation results to target unit system.\n\nParameters\n----------\nresult : array-like or UWQuantity\n Evaluation result to convert\ntarget_units : str or Pint unit\n Target units to convert to\n\nReturns\n-------\nconverted result\n Result converted to target units", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "add_units", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 874, + "signature": "(array, units_str)", + "parameters": [ + { + "name": "array", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units_str", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add unit metadata to plain array.\n\nParameters\n----------\narray : array-like\n Plain array to add units to\nunits_str : str\n Unit string to associate with array\n\nReturns\n-------\nUWQuantity\n Array wrapped with unit information", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "make_evaluate_unit_aware", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 895, + "signature": "(original_evaluate_func)", + "parameters": [ + { + "name": "original_evaluate_func", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Decorator to make evaluate functions unit-aware with explicit unit specification.\n\nThis wraps the original evaluate function to:\n1. Accept explicit coordinate units parameter\n2. Convert coordinates to mesh units if units are specified\n3. Assume model coordinates if no units specified\n4. Convert results back to appropriate physical units\n\nFollowing UW3 policy: no implicit unit detection or conversion.\n\nParameters\n----------\noriginal_evaluate_func : callable\n Original evaluate function to wrap\n\nReturns\n-------\ncallable\n Unit-aware version of the function", + "harvested_comments": [ + "Auto-extract .sym from MeshVariable for user convenience", + "This is likely a MeshVariable - extract the symbolic representation", + "Handle the case where no coordinates are provided", + "Get mesh coordinate unit information", + "Approach 1: Use coord_sys if provided" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "set_property", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 75, + "signature": "(self, prop: Union[MaterialProperty, str], value: Any)", + "parameters": [ + { + "name": "prop", + "type_hint": "Union[MaterialProperty, str]", + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": "Any", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set a material property value", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialDefinition", + "is_public": true + }, + { + "name": "get_property", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 80, + "signature": "(self, prop: Union[MaterialProperty, str], default = None)", + "parameters": [ + { + "name": "prop", + "type_hint": "Union[MaterialProperty, str]", + "default": null, + "description": "" + }, + { + "name": "default", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get a material property value", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialDefinition", + "is_public": true + }, + { + "name": "has_property", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 85, + "signature": "(self, prop: Union[MaterialProperty, str]) -> bool", + "parameters": [ + { + "name": "prop", + "type_hint": "Union[MaterialProperty, str]", + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if material has a specific property", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialDefinition", + "is_public": true + }, + { + "name": "evaluate_property", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 90, + "signature": "(self, prop: Union[MaterialProperty, str], temperature = None, pressure = None)", + "parameters": [ + { + "name": "prop", + "type_hint": "Union[MaterialProperty, str]", + "default": null, + "description": "" + }, + { + "name": "temperature", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "pressure", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Evaluate a material property, accounting for temperature/pressure dependence.\n\nParameters:\n-----------\nprop : MaterialProperty or str\n Property to evaluate\ntemperature : float or array, optional\n Temperature for evaluation\npressure : float or array, optional\n Pressure for evaluation\n\nReturns:\n--------\nProperty value (scalar or array)", + "harvested_comments": [ + "Get base value", + "Apply temperature dependence", + "Apply pressure dependence" + ], + "status": "complete", + "needs": [], + "parent_class": "MaterialDefinition", + "is_public": true + }, + { + "name": "create_material", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 156, + "signature": "(self, name: str, description: str = '', reference: str = '') -> MaterialDefinition", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "description", + "type_hint": "str", + "default": "''", + "description": "" + }, + { + "name": "reference", + "type_hint": "str", + "default": "''", + "description": "" + } + ], + "returns": "MaterialDefinition", + "existing_docstring": "Create a new material definition.\n\nParameters:\n-----------\nname : str\n Material name\ndescription : str\n Human-readable description\nreference : str\n Literature reference\n\nReturns:\n--------\nMaterialDefinition\n New material instance", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "get_material", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 186, + "signature": "(self, name: str) -> Optional[MaterialDefinition]", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "Optional[MaterialDefinition]", + "existing_docstring": "Get a material by name", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "list_materials", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 190, + "signature": "(self) -> List[str]", + "parameters": [], + "returns": "List[str]", + "existing_docstring": "List all material names", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "delete_material", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 194, + "signature": "(self, name: str)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Delete a material definition", + "harvested_comments": [ + "Remove any region assignments" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "assign_to_region", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 206, + "signature": "(self, material_name: str, region_id: int)", + "parameters": [ + { + "name": "material_name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "region_id", + "type_hint": "int", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Assign a material to a mesh region.\n\nParameters:\n-----------\nmaterial_name : str\n Name of material to assign\nregion_id : int\n Mesh region identifier", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "get_region_material", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 223, + "signature": "(self, region_id: int) -> Optional[str]", + "parameters": [ + { + "name": "region_id", + "type_hint": "int", + "default": null, + "description": "" + } + ], + "returns": "Optional[str]", + "existing_docstring": "Get the material assigned to a region", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "get_material_regions", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 227, + "signature": "(self, material_name: str) -> List[int]", + "parameters": [ + { + "name": "material_name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "List[int]", + "existing_docstring": "Get all regions assigned to a material", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "evaluate_property_field", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 235, + "signature": "(self, prop: Union[MaterialProperty, str], region_field: np.ndarray, temperature: Optional[np.ndarray] = None, pressure: Optional[np.ndarray] = None) -> np.ndarray", + "parameters": [ + { + "name": "prop", + "type_hint": "Union[MaterialProperty, str]", + "default": null, + "description": "" + }, + { + "name": "region_field", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "temperature", + "type_hint": "Optional[np.ndarray]", + "default": "None", + "description": "" + }, + { + "name": "pressure", + "type_hint": "Optional[np.ndarray]", + "default": "None", + "description": "" + } + ], + "returns": "np.ndarray", + "existing_docstring": "Evaluate a material property over a field of region IDs.\n\nParameters:\n-----------\nprop : MaterialProperty or str\n Property to evaluate\nregion_field : array\n Array of region IDs\ntemperature : array, optional\n Temperature field for evaluation\npressure : array, optional\n Pressure field for evaluation\n\nReturns:\n--------\narray\n Property values corresponding to each region", + "harvested_comments": [ + "Initialize output array", + "Evaluate property for each unique region", + "Get mask for this region", + "Extract temperature/pressure for this region if provided", + "Evaluate property for this region" + ], + "status": "complete", + "needs": [], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "add_callback", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 293, + "signature": "(self, callback: Callable)", + "parameters": [ + { + "name": "callback", + "type_hint": "Callable", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add a callback function for material changes.\n\nParameters\n----------\ncallback : callable\n Function called as ``callback(event_type, *args)``", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "export_config", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 312, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "Export material configuration", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "import_config", + "kind": "method", + "file": "src/underworld3/materials.py", + "line": 327, + "signature": "(self, config: Dict[str, Any])", + "parameters": [ + { + "name": "config", + "type_hint": "Dict[str, Any]", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Import material configuration from exported dict", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MaterialRegistry", + "is_public": true + }, + { + "name": "create_standard_mantle_material", + "kind": "function", + "file": "src/underworld3/materials.py", + "line": 344, + "signature": "(registry: MaterialRegistry) -> MaterialDefinition", + "parameters": [ + { + "name": "registry", + "type_hint": "MaterialRegistry", + "default": null, + "description": "" + } + ], + "returns": "MaterialDefinition", + "existing_docstring": "Create a standard mantle material with typical properties", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_standard_crust_material", + "kind": "function", + "file": "src/underworld3/materials.py", + "line": 361, + "signature": "(registry: MaterialRegistry) -> MaterialDefinition", + "parameters": [ + { + "name": "registry", + "type_hint": "MaterialRegistry", + "default": null, + "description": "" + } + ], + "returns": "MaterialDefinition", + "existing_docstring": "Create a standard crustal material with typical properties", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_high_viscosity_material", + "kind": "function", + "file": "src/underworld3/materials.py", + "line": 378, + "signature": "(registry: MaterialRegistry, name: str = 'high_visc', viscosity_contrast: float = 1000) -> MaterialDefinition", + "parameters": [ + { + "name": "registry", + "type_hint": "MaterialRegistry", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "'high_visc'", + "description": "" + }, + { + "name": "viscosity_contrast", + "type_hint": "float", + "default": "1000", + "description": "" + } + ], + "returns": "MaterialDefinition", + "existing_docstring": "Create a high viscosity material for inclusion studies", + "harvested_comments": [ + "Base properties similar to mantle" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "delta", + "kind": "function", + "file": "src/underworld3/maths/functions.py", + "line": 21, + "signature": "(x: sympy.Basic, epsilon: float)", + "parameters": [ + { + "name": "x", + "type_hint": "sympy.Basic", + "default": null, + "description": "" + }, + { + "name": "epsilon", + "type_hint": "float", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Smoothed (Gaussian) approximation to the Dirac delta function.\n\nReturns a Gaussian with integral 1, approximating :math:`\\delta(x)`\nas :math:`\\epsilon \\to 0`:\n\n.. math::\n\n \\delta_\\epsilon(x) = \\frac{1}{\\epsilon\\sqrt{2\\pi}}\n \\exp\\left(-\\frac{x^2}{2\\epsilon^2}\\right)\n\nParameters\n----------\nx : sympy.Basic\n Symbolic expression (typically a coordinate or distance function).\nepsilon : float\n Smoothing width. Smaller values give sharper peaks.\n\nReturns\n-------\nsympy.Expr\n Gaussian approximation to the delta function.\n\nNotes\n-----\nUseful for representing interfaces, point sources, or boundary layers\nin a regularized form suitable for finite element integration.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "L2_norm", + "kind": "function", + "file": "src/underworld3/maths/functions.py", + "line": 59, + "signature": "(n_s, a_s, mesh)", + "parameters": [ + { + "name": "n_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "L2 norm of the difference between numerical and analytical solutions.\n\nComputes:\n\n.. math::\n\n \\|n - a\\|_{L^2} = \\sqrt{\\int_\\Omega (n - a)^2 \\, d\\Omega}\n\nFor vector fields, uses the dot product:\n\n.. math::\n\n \\|\\mathbf{n} - \\mathbf{a}\\|_{L^2} = \\sqrt{\\int_\\Omega\n (\\mathbf{n} - \\mathbf{a}) \\cdot (\\mathbf{n} - \\mathbf{a}) \\, d\\Omega}\n\nParameters\n----------\nn_s : sympy.Expr or sympy.Matrix\n Numerical solution (scalar or vector field).\na_s : sympy.Expr or sympy.Matrix\n Analytical solution (scalar or vector field).\nmesh : Mesh\n The mesh over which to integrate.\n\nReturns\n-------\nfloat\n L2 norm of the error.", + "harvested_comments": [ + "Check if the input is a vector (SymPy Matrix)", + "Compute squared difference using dot product for vectors", + "Compute squared difference for scalars", + "Integral over the domain", + "Compute the L2 norm" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "rank2_symmetric_sym", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 45, + "signature": "(name, dim)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Rank 2 symmetric tensor (as symbolic matrix), name is a sympy latex expression", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rank4_symmetric_sym", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 58, + "signature": "(name, dim)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Rank 4 symmetric tensor (as symbolic matrix), name is a sympy latex expression", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "tensor_rotation", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 77, + "signature": "(R, T)", + "parameters": [ + { + "name": "R", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "T", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Rotate tensor of any rank using matrix R", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rank2_to_voigt", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 192, + "signature": "(v_ij, dim, covariant = True)", + "parameters": [ + { + "name": "v_ij", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "covariant", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert rank-2 tensor :math:`v_{ij}` to Voigt (vector) form :math:`V_I`.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "voigt_to_rank2", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 210, + "signature": "(V_I, dim, covariant = True)", + "parameters": [ + { + "name": "V_I", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "covariant", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert to rank 2 tensor (v_ij) from voigt (vector) form (V_I)", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rank4_to_voigt", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 228, + "signature": "(c_ijkl, dim)", + "parameters": [ + { + "name": "c_ijkl", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert rank 4 tensor (c_ijkl) to voigt (matrix) form (C_IJ)", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "voigt_to_rank4", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 240, + "signature": "(C_IJ, dim)", + "parameters": [ + { + "name": "C_IJ", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert to rank 4 tensor (c_ijkl) from voigt (matrix) form (C_IJ)", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rank2_to_mandel", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 249, + "signature": "(v_ij, dim)", + "parameters": [ + { + "name": "v_ij", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert rank-2 tensor to Mandel vector form.\n\nMandel notation scales off-diagonal terms by :math:`\\sqrt{2}`,\npreserving inner products under vector operations.\n\nParameters\n----------\nv_ij : sympy.Matrix\n Symmetric rank-2 tensor as (dim x dim) matrix.\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.Matrix\n Mandel vector (3 components for 2D, 6 for 3D).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "rank4_to_mandel", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 275, + "signature": "(c_ijkl, dim)", + "parameters": [ + { + "name": "c_ijkl", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert rank-4 tensor to Mandel matrix form.\n\nParameters\n----------\nc_ijkl : sympy.NDimArray\n Symmetric rank-4 tensor (dim x dim x dim x dim).\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.Matrix\n Mandel matrix (3x3 for 2D, 6x6 for 3D).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "mandel_to_rank2", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 297, + "signature": "(v_I, dim)", + "parameters": [ + { + "name": "v_I", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert Mandel vector to rank-2 tensor form.\n\nParameters\n----------\nv_I : sympy.Matrix\n Mandel vector (3 components for 2D, 6 for 3D).\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.Matrix\n Symmetric rank-2 tensor as (dim x dim) matrix.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "mandel_to_rank4", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 318, + "signature": "(c_IJ, dim)", + "parameters": [ + { + "name": "c_IJ", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert Mandel matrix to rank-4 tensor form.\n\nParameters\n----------\nc_IJ : sympy.Matrix\n Mandel matrix (3x3 for 2D, 6x6 for 3D).\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.NDimArray\n Symmetric rank-4 tensor (dim x dim x dim x dim).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "rank4_identity", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 339, + "signature": "(dim)", + "parameters": [ + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Symmetric fourth-order identity tensor.\n\nConstructs the identity tensor for symmetric second-order tensors:\n\n.. math::\n\n I_{ijkl} = \\frac{1}{2}(\\delta_{ik}\\delta_{jl} + \\delta_{il}\\delta_{jk})\n\nThis tensor satisfies :math:`I_{ijkl} \\sigma_{kl} = \\sigma_{ij}` for\nsymmetric :math:`\\sigma`.\n\nParameters\n----------\ndim : int\n Spatial dimension (2 or 3).\n\nReturns\n-------\nsympy.NDimArray\n Fourth-order identity tensor (dim x dim x dim x dim).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "rank2_inner_product", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 373, + "signature": "(A, B)", + "parameters": [ + { + "name": "A", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "B", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Double contraction (inner product) of two rank-2 tensors.\n\nComputes:\n\n.. math::\n\n p = \\sum_i \\sum_j A_{ij} B_{ij} = A : B\n\nParameters\n----------\nA : sympy.Matrix or sympy.NDimArray\n First rank-2 tensor.\nB : sympy.Matrix or sympy.NDimArray\n Second rank-2 tensor.\n\nReturns\n-------\nsympy.Expr\n Scalar result of the double contraction.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "cross", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 67, + "signature": "(self, vector1, vector2)", + "parameters": [ + { + "name": "vector1", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vector2", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Cross product of two vector fields.\n\nParameters\n----------\nvector1 : sympy.Matrix\n First vector as row matrix.\nvector2 : sympy.Matrix\n Second vector as row matrix.\n\nReturns\n-------\nsympy.Matrix\n Cross product :math:`\\mathbf{a} \\times \\mathbf{b}` as row matrix.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "curl", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 91, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Curl of a vector field: :math:`\\nabla \\times \\mathbf{v}`.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix.\n\nReturns\n-------\nsympy.Matrix or sympy.Expr\n In 3D: curl vector as row matrix.\n In 2D: scalar (out-of-plane component, i.e., vorticity).", + "harvested_comments": [ + "if 2d, the out-of-plane vector is not defined in the basis so a scalar is returned (cf. vorticity)" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "divergence", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 116, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Divergence of a vector field: :math:`\\nabla \\cdot \\mathbf{v}`.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "gradient", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 134, + "signature": "(self, scalar)", + "parameters": [ + { + "name": "scalar", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Gradient of a scalar field: :math:`\\nabla \\phi`.\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "strain_tensor", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 154, + "signature": "(self, vector)", + "parameters": [ + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Symmetric gradient (strain or strain-rate tensor).\n\nComputes the infinitesimal strain tensor from displacement,\nor strain-rate tensor from velocity:\n\n.. math::\n\n \\varepsilon_{ij} = \\frac{1}{2}\\left(\\frac{\\partial u_i}{\\partial x_j}\n + \\frac{\\partial u_j}{\\partial x_i}\\right)\n\nParameters\n----------\nvector : sympy.Matrix or sympy.vector.Vector\n Displacement or velocity field.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (dim x dim) strain tensor.", + "harvested_comments": [ + "Coerce vector to sympy.Matrix form" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "to_vector", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 184, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert row matrix to SymPy vector form.\n\nParameters\n----------\nmatrix : sympy.Matrix or sympy.vector.Vector\n Vector as row or column matrix.\n\nReturns\n-------\nsympy.vector.Vector\n Vector in the mesh's coordinate system basis.", + "harvested_comments": [ + "No need to convert", + "Note, the mesh vector basis is always 3D so out-of-plane", + "vectors are allowed." + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "to_matrix", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 216, + "signature": "(self, vector)", + "parameters": [ + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert SymPy vector to row matrix form.\n\nParameters\n----------\nvector : sympy.vector.Vector or sympy.Matrix\n Vector to convert.\n\nReturns\n-------\nsympy.Matrix\n Row matrix (1 x dim) representation.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "jacobian", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 247, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Jacobian matrix of a field with respect to mesh coordinates.\n\nParameters\n----------\nmatrix : sympy.Matrix or sympy.vector.Vector\n Scalar, vector, or matrix field.\n\nReturns\n-------\nsympy.Matrix\n Jacobian matrix of partial derivatives.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus", + "is_public": true + }, + { + "name": "divergence", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 302, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Divergence of a vector field in cylindrical coordinates.\n\nComputes :math:`\\nabla \\cdot \\mathbf{v}` with the cylindrical\nmetric terms:\n\n.. math::\n\n \\nabla \\cdot \\mathbf{v} = \\frac{\\partial v_r}{\\partial r}\n + \\frac{v_r}{r} + \\frac{1}{r}\\frac{\\partial v_\\theta}{\\partial \\theta}\n + \\frac{\\partial v_z}{\\partial z}\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_z)`.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence.", + "harvested_comments": [ + "Or is this cdim ?" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_cylindrical", + "is_public": true + }, + { + "name": "gradient", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 340, + "signature": "(self, scalar)", + "parameters": [ + { + "name": "scalar", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Gradient of a scalar field in cylindrical coordinates.\n\nComputes :math:`\\nabla \\phi` with the cylindrical metric:\n\n.. math::\n\n \\nabla \\phi = \\frac{\\partial \\phi}{\\partial r}\\hat{r}\n + \\frac{1}{r}\\frac{\\partial \\phi}{\\partial \\theta}\\hat{\\theta}\n + \\frac{\\partial \\phi}{\\partial z}\\hat{z}\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix.", + "harvested_comments": [ + "Or is this cdim ?" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_cylindrical", + "is_public": true + }, + { + "name": "curl", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 380, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Curl of a vector field in cylindrical coordinates.\n\nComputes :math:`\\nabla \\times \\mathbf{v}` with cylindrical metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_z)`.\n\nReturns\n-------\nsympy.Matrix or sympy.Expr\n In 3D: curl vector as row matrix.\n In 2D: scalar (out-of-plane vorticity component).", + "harvested_comments": [ + "if 2D, return a scalar of the out-of-plane curl" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_cylindrical", + "is_public": true + }, + { + "name": "strain_tensor", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 420, + "signature": "(self, vector)", + "parameters": [ + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Strain tensor in cylindrical coordinates.\n\nComputes the symmetric gradient with cylindrical metric corrections.\nAdditional terms arise from coordinate curvature.\n\nParameters\n----------\nvector : sympy.Matrix\n Displacement or velocity field :math:`(v_r, v_\\theta, v_z)`.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (dim x dim) strain tensor with cylindrical corrections.", + "harvested_comments": [ + "Coerce vector to sympy.Matrix form", + "E_00, E_22 and E_02 are unchanged from Cartesian", + "E[0,0] = L[0,0]" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_cylindrical", + "is_public": true + }, + { + "name": "divergence", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 628, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Divergence of a vector field in spherical coordinates.\n\nComputes :math:`\\nabla \\cdot \\mathbf{v}` with spherical metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_\\phi)`.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence.", + "harvested_comments": [ + "cosecant_th = sympy.Piecewise(", + "(1000, sympy.Abs(t) < 0.01 * sympy.pi), (1 / sympy.sin(t), True)" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical", + "is_public": true + }, + { + "name": "gradient", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 667, + "signature": "(self, scalar)", + "parameters": [ + { + "name": "scalar", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Gradient of a scalar field in spherical coordinates.\n\nComputes :math:`\\nabla \\phi` with spherical metric terms.\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix :math:`(\\partial_r, \\partial_\\theta/r, \\partial_\\phi/(r\\sin\\theta))`.", + "harvested_comments": [ + "grad_S[2] = sympy.Piecewise(", + "(1000, sympy.Abs(p) < 0.01 * sympy.pi),", + "(scalar.diff(p) / (r * sympy.sin(t)), True)," + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical", + "is_public": true + }, + { + "name": "curl", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 704, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Curl of a vector field in spherical coordinates.\n\nComputes :math:`\\nabla \\times \\mathbf{v}` with spherical metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_r, v_\\theta, v_\\phi)`.\n\nReturns\n-------\nsympy.Matrix\n Curl vector as row matrix.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical", + "is_public": true + }, + { + "name": "strain_tensor", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 739, + "signature": "(self, vector)", + "parameters": [ + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Strain tensor in spherical coordinates.\n\nComputes the symmetric gradient with spherical metric corrections\nfor :math:`(r, \\theta, \\phi)` coordinates.\n\nParameters\n----------\nvector : sympy.Matrix\n Displacement or velocity field :math:`(v_r, v_\\theta, v_\\phi)`.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (3 x 3) strain tensor with spherical corrections.", + "harvested_comments": [ + "Coerce vector to sympy.Matrix form" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical", + "is_public": true + }, + { + "name": "divergence", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 819, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Divergence of a vector field on a spherical surface.\n\nComputes :math:`\\nabla \\cdot \\mathbf{v}` with surface metric terms.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_\\lambda, v_\\phi)`.\n\nReturns\n-------\nsympy.Expr\n Scalar divergence on the surface.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "is_public": true + }, + { + "name": "gradient", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 854, + "signature": "(self, scalar)", + "parameters": [ + { + "name": "scalar", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Gradient of a scalar field on a spherical surface.\n\nComputes :math:`\\nabla \\phi` on the unit sphere.\n\nParameters\n----------\nscalar : sympy.Expr or sympy.Matrix\n Scalar field. If (1,1) matrix, extracts the scalar.\n\nReturns\n-------\nsympy.Matrix\n Gradient vector as row matrix :math:`(\\partial_\\lambda/\\cos\\phi, \\partial_\\phi)`.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "is_public": true + }, + { + "name": "curl", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 887, + "signature": "(self, matrix)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Curl on a spherical surface.\n\nComputes the scalar vorticity (out-of-surface curl component)\non the unit sphere.\n\nParameters\n----------\nmatrix : sympy.Matrix\n Vector field as row matrix :math:`(v_\\lambda, v_\\phi)`.\n\nReturns\n-------\nsympy.Expr\n Scalar vorticity (radial component of curl).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "is_public": true + }, + { + "name": "strain_tensor", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 920, + "signature": "(self, vector)", + "parameters": [ + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Strain tensor on a spherical surface.\n\nComputes the symmetric gradient on the unit sphere with\nsurface metric corrections.\n\nParameters\n----------\nvector : sympy.Matrix\n Displacement or velocity field :math:`(v_\\lambda, v_\\phi)`.\n\nReturns\n-------\nsympy.Matrix\n Symmetric (2 x 2) strain tensor on the surface.", + "harvested_comments": [ + "Coerce vector to sympy.Matrix form", + "E_00, E_22 and E_02 are unchanged from Cartesian" + ], + "status": "complete", + "needs": [], + "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "is_public": true + }, + { + "name": "QuarterAnnulus", + "kind": "function", + "file": "src/underworld3/meshing/annulus.py", + "line": 33, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, angle: float = 45, cellSize: float = 0.1, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "angle", + "type_hint": "float", + "default": "45", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "centre", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a quarter-annulus (wedge) mesh in 2D.\n\nGenerates a pie-slice shaped mesh bounded by inner and outer circular\narcs and two radial edges. Provides :math:`(r, \\theta)` coordinates\nfor convenient representation of radial problems.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the annulus. Supports UWQuantity objects for\n automatic unit conversion.\nradiusInner : float, default=0.547\n Inner radius of the annulus. Set to 0 for a disc sector\n extending to the centre.\nangle : float, default=45\n Angular extent of the wedge in degrees. The wedge spans from\n ``-angle`` to ``+angle`` (total sweep = 2 * angle).\ncellSize : float, default=0.1\n Target mesh element size.\ncentre : bool, default=False\n If True and ``radiusInner=0``, mark the centre point as a\n boundary for applying point constraints.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner arc at :math:`r = r_{inner}`\n - ``Upper``: Outer arc at :math:`r = r_{outer}`\n - ``Left``: Left radial edge\n - ``Right``: Right radial edge\n - ``Centre``: Centre point (if :math:`r_{inner} = 0`)\n\nSee Also\n--------\nAnnulus : Full 360-degree annulus.\nSegmentofAnnulus : Partial annulus with arbitrary angle.\n\nExamples\n--------\nCreate a quarter-disc (no inner hole):\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.QuarterAnnulus(\n... radiusOuter=1.0,\n... radiusInner=0.0,\n... angle=45,\n... cellSize=0.05\n... )\n\nNotes\n-----\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: tangential direction :math:`(\\theta)`", + "harvested_comments": [ + "Convert unit-aware quantities to non-dimensional units", + "This enables: QuarterAnnulus(radiusOuter=uw.quantity(6370, \"km\"), ...)", + "angle is in degrees, not a length - don't convert", + "gmsh.model.geo.rotate([(p2, p3)], 0, 0, 0, 0, 0.3, 0, math.pi / 2)", + "add boundary normal information to the new mesh" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "Annulus", + "kind": "function", + "file": "src/underworld3/meshing/annulus.py", + "line": 273, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, cellSizeOuter: float = None, cellSizeInner: float = None, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "cellSizeOuter", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "cellSizeInner", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "centre", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a full 2D annulus mesh.\n\nGenerates a ring-shaped mesh (or disc if ``radiusInner=0``) using\ntriangular elements. Provides :math:`(r, \\theta)` coordinates with\nradial boundary normal directions.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the annulus. Supports UWQuantity objects.\nradiusInner : float, default=0.547\n Inner radius of the annulus. Set to 0 for a full disc.\ncellSize : float, default=0.1\n Default target mesh element size.\ncellSizeOuter : float, optional\n Element size at the outer boundary. Defaults to ``cellSize``.\ncellSizeInner : float, optional\n Element size at the inner boundary. Defaults to ``cellSize``.\n Use different sizes to create graded meshes.\ncentre : bool, default=False\n If True and ``radiusInner=0``, mark the centre point as a\n boundary for applying point constraints.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply. Each level\n approximately quadruples element count.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner boundary at :math:`r = r_{inner}`\n - ``Upper``: Outer boundary at :math:`r = r_{outer}`\n - ``Centre``: Centre point (if :math:`r_{inner} = 0`)\n\n The mesh includes a refinement callback that snaps boundary\n nodes back to true circular geometry after refinement.\n\nSee Also\n--------\nQuarterAnnulus : Partial annulus with radial edges.\nAnnulusInternalBoundary : Annulus with an internal interface.\nSphericalShell : 3D equivalent for spherical geometries.\n\nExamples\n--------\nCreate a standard annulus for mantle convection:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.Annulus(\n... radiusOuter=1.0,\n... radiusInner=0.547,\n... cellSize=0.05\n... )\n\nCreate a graded mesh (finer at inner boundary):\n\n>>> mesh = uw.meshing.Annulus(\n... radiusOuter=1.0,\n... radiusInner=0.5,\n... cellSizeOuter=0.1,\n... cellSizeInner=0.02\n... )\n\nNotes\n-----\nThe inner radius default of 0.547 corresponds approximately to the\nEarth's core-mantle boundary radius ratio (3480/6371).\n\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: tangential direction :math:`(\\theta)`\n\nFor free-slip boundary conditions on vector problems (e.g., Stokes),\nuse a penalty on the normal velocity component::\n\n Gamma_N = mesh.Gamma # discrete normal, or\n Gamma_N = mesh.CoordinateSystem.unit_e_0 # analytic radial\n stokes.add_natural_bc(\n penalty * Gamma_N.dot(v_soln.sym) * Gamma_N, \"Upper\"\n )", + "harvested_comments": [ + "discrete normal, or", + "analytic radial", + "Convert unit-aware quantities to non-dimensional units", + "This enables: Annulus(radiusOuter=uw.quantity(6370, \"km\"), ...)", + "l1 = gmsh.model.geo.add_line(p5, p4)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SegmentofAnnulus", + "kind": "function", + "file": "src/underworld3/meshing/annulus.py", + "line": 556, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, angleExtent: float = 45, cellSize: float = 0.1, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "angleExtent", + "type_hint": "float", + "default": "45", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "centre", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Generates a segment of an annulus using Gmsh. This function creates a 2D mesh of an annular segment defined by outer and inner radii,\nand the extent of the angle. The mesh can be customized with various parameters like cell size, element degree, and verbosity.\n\nParameters\n----------\nradiusOuter : float, optional\n The outer radius of the annular segment. Default is 1.0.\nradiusInner : float, optional\n The inner radius of the annular segment. Default is 0.547.\nangleExtent : float, optional\n The angular extent of the segment in degrees. Default is 45.\ncellSize : float, optional\n The target size for the mesh elements. Default is 0.1.\ncentre : bool, optional\n If True, the segment will be centered at the origin. Default is False.\ndegree : int, optional\n The polynomial degree of the finite elements. Default is 1.\nqdegree : int, optional\n The quadrature degree for integration. Default is 2.\nfilename : str, optional\n The name of the file where the mesh will be saved. Default is None.\nrefinement : optional\n Refinement level for the mesh. Default is None.\ngmsh_verbosity : int, optional\n Gmsh output verbosity (0=quiet). Default is 0.\nverbose : bool, optional\n If True, print additional information. Default is False.\n\nReturns\n-------\nMesh\n The generated annular segment mesh.\n\nExamples\n--------\n>>> mesh = uw.meshing.SegmentofAnnulus(\n... radiusOuter=2.0,\n... radiusInner=1.0,\n... angleExtent=90.0,\n... cellSize=0.05,\n... centre=True,\n... )", + "harvested_comments": [ + "Convert unit-aware quantities to non-dimensional units", + "angleExtent is in degrees, not a length - don't convert", + "error checks", + "angle Extent in radian", + "gmsh.model.mesh.embed(0, [p0], 2, s) # not sure use of this line" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "AnnulusWithSpokes", + "kind": "function", + "file": "src/underworld3/meshing/annulus.py", + "line": 798, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSizeOuter: float = 0.1, cellSizeInner: float = None, centre: bool = False, spokes: int = 3, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSizeOuter", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "cellSizeInner", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "centre", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "spokes", + "type_hint": "int", + "default": "3", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create an annulus mesh divided into sectors by radial spokes.\n\nGenerates a ring-shaped mesh with radial internal boundaries (spokes)\nthat divide the annulus into equal angular sectors. Useful for problems\nrequiring sector-wise analysis or periodic boundary conditions.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the annulus.\nradiusInner : float, default=0.547\n Inner radius of the annulus.\ncellSizeOuter : float, default=0.1\n Element size at the outer boundary.\ncellSizeInner : float, optional\n Element size at the inner boundary. Defaults to ``cellSizeOuter``.\ncentre : bool, default=False\n Mark the centre point as a boundary (if radiusInner=0).\nspokes : int, default=3\n Number of radial spokes dividing the annulus. Creates ``spokes``\n equal angular sectors.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner boundary at :math:`r = r_{inner}`\n - ``LowerPlus``: Inner boundary + spokes (for BCs)\n - ``Upper``: Outer boundary at :math:`r = r_{outer}`\n - ``UpperPlus``: Outer boundary + spokes (for BCs)\n - ``Spokes``: Radial spoke boundaries\n\nSee Also\n--------\nAnnulus : Standard annulus without spokes.\n\nExamples\n--------\nCreate a 6-sector annulus:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.AnnulusWithSpokes(\n... radiusOuter=1.0,\n... radiusInner=0.5,\n... spokes=6,\n... cellSizeOuter=0.05\n... )\n\nNotes\n-----\nThe ``LowerPlus`` and ``UpperPlus`` boundaries are composite labels\nthat include both the curved boundaries and the spoke edges, useful\nfor applying no-slip conditions that include radial walls.", + "harvested_comments": [ + "Convert unit-aware quantities to non-dimensional units", + "spokes is a count, not a length - don't convert", + "Now copy / rotate", + "We finally generate and save the mesh:", + "We need to build the plex here in order to make some changes" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "AnnulusInternalBoundary", + "kind": "function", + "file": "src/underworld3/meshing/annulus.py", + "line": 1132, + "signature": "(radiusOuter: float = 1.5, radiusInternal: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, cellSize_Outer: float = None, cellSize_Inner: float = None, cellSize_Internal: float = None, centre: bool = False, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.5", + "description": "" + }, + { + "name": "radiusInternal", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "cellSize_Outer", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "cellSize_Inner", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "cellSize_Internal", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "centre", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create an annulus mesh with a circular internal boundary.\n\nGenerates an annular mesh with an embedded internal boundary at a\nspecified radius, useful for tracking flux across interfaces or\ndefining different material layers.\n\nParameters\n----------\nradiusOuter : float, default=1.5\n Outer radius of the annulus.\nradiusInternal : float, default=1.0\n Radius of the internal boundary surface.\nradiusInner : float, default=0.547\n Inner radius of the annulus.\ncellSize : float, default=0.1\n Default target mesh element size.\ncellSize_Outer : float, optional\n Element size at the outer boundary.\ncellSize_Inner : float, optional\n Element size at the inner boundary.\ncellSize_Internal : float, optional\n Element size at the internal boundary.\ncentre : bool, default=False\n Mark the centre point as a boundary (if radiusInner=0).\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner boundary at :math:`r = r_{inner}`\n - ``Internal``: Internal boundary at :math:`r = r_{internal}`\n - ``Upper``: Outer boundary at :math:`r = r_{outer}`\n - ``Centre``: Centre point (if radiusInner=0)\n\nSee Also\n--------\nAnnulus : Annulus without internal boundary.\nBoxInternalBoundary : Cartesian box with internal boundary.\nSphericalShellInternalBoundary : 3D spherical equivalent.\n\nExamples\n--------\nCreate an annulus with an internal tracking surface:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.AnnulusInternalBoundary(\n... radiusOuter=1.0,\n... radiusInternal=0.7,\n... radiusInner=0.5,\n... cellSize=0.03\n... )\n\nNotes\n-----\nThe internal boundary is useful for calculating radial heat or\nmass flux across a specified radius (e.g., core-mantle boundary,\nthermal boundary layer).", + "harvested_comments": [ + "Convert unit-aware quantities to non-dimensional units", + "Outermost mesh", + "Create two surfaces sharing the internal boundary (no embed needed)", + "Boundary physical groups (1D)", + "Region physical groups (2D) \u2014 labels cells by region" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "DiscInternalBoundaries", + "kind": "function", + "file": "src/underworld3/meshing/annulus.py", + "line": 1428, + "signature": "(radiusUpper: float = 1.5, radiusInternal: float = 1.0, radiusLower: float = 0.547, cellSize: float = 0.1, cellSize_Upper: float = None, cellSize_Lower: float = None, cellSize_Internal: float = None, cellSize_Centre: float = None, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusUpper", + "type_hint": "float", + "default": "1.5", + "description": "" + }, + { + "name": "radiusInternal", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusLower", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "cellSize_Upper", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "cellSize_Lower", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "cellSize_Internal", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "cellSize_Centre", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a disc mesh with multiple concentric internal boundaries.\n\nGenerates a full disc (extending to centre) with two embedded\ncircular internal boundaries at specified radii. Useful for\nmulti-layer problems or tracking flux across multiple interfaces.\n\nParameters\n----------\nradiusUpper : float, default=1.5\n Outer radius of the disc.\nradiusInternal : float, default=1.0\n Radius of the middle internal boundary.\nradiusLower : float, default=0.547\n Radius of the inner internal boundary.\ncellSize : float, default=0.1\n Default target mesh element size.\ncellSize_Upper : float, optional\n Element size at the outer boundary.\ncellSize_Lower : float, optional\n Element size at the lower internal boundary.\ncellSize_Internal : float, optional\n Element size at the middle internal boundary.\ncellSize_Centre : float, optional\n Element size at the disc centre.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D mesh with boundaries:\n\n - ``Lower``: Inner internal boundary at :math:`r = r_{lower}`\n - ``Internal``: Middle internal boundary at :math:`r = r_{internal}`\n - ``Upper``: Outer boundary at :math:`r = r_{upper}`\n - ``Centre``: Centre point\n\nSee Also\n--------\nAnnulusInternalBoundary : Annulus with one internal boundary.\n\nExamples\n--------\nCreate a disc with two tracking surfaces:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.DiscInternalBoundaries(\n... radiusUpper=1.0,\n... radiusInternal=0.7,\n... radiusLower=0.4,\n... cellSize=0.03\n... )\n\nNotes\n-----\nUnlike AnnulusInternalBoundary, this mesh extends to the centre\n(no inner hole). All internal boundaries are embedded as mesh\nedges, allowing flux integration across each interface.", + "harvested_comments": [ + "Convert unit-aware quantities to non-dimensional units", + "loops = [cl1] + loops", + "Outermost mesh", + "# Now add embedded surfaces", + "# This is the same as the simple annulus" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "normals", + "kind": "method", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 118, + "signature": "(self, coords)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Outward unit normals at ``coords`` from the projected P1\nboundary-normal field (``mesh.Gamma_P1``).\n\nReturns ``(normals, valid)`` where ``valid`` is False at nodes whose\nprojected normal is degenerate (box corners, unlocatable points) \u2014 those\nshould be pinned, not slipped.\n\nDESIGN NOTE (2026-06 \u2014 breadcrumb for a future session). This\nRE-SOLVES the ``Gamma_P1`` projection on the surface's *current* mesh\nstate (``_slip_normals`` calls ``mesh._update_projected_normals()``),\nso the normal follows mesh deformation \u2014 the state-aware behaviour the\n``free`` / ``release()`` surface-follow mode is designed to use. The\nretired ``_build_slip_projector`` instead read a *cached* ``_n_proj``\nfield (the deleted ``_gamma_p1_at_vertices`` helper: KDTree match, no\nsolve, normal frozen at the reference mesh) \u2014 cheaper but not\ndeformation-aware. ``mesh.boundary_slip`` calls this ONCE per build\n(not per ``project()`` call), so the cost is ~one projection solve per\nmover outer-iteration; parity with the cached path was machine\nprecision (~1e-16) on a centred annulus. **If this ever shows up as a\nhot-path regression (fine meshes / many adapts), add a cached\nfast-path here** \u2014 read ``mesh._projected_normals.data`` directly (as\n``_gamma_p1_at_vertices`` did in git history) and re-solve only for\nfree surfaces. See docs/developer/design/boundary-slip-strategy.md.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "BoundingSurface", + "is_public": true + }, + { + "name": "tangent_project", + "kind": "method", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 147, + "signature": "(self, coords, reference)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "reference", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Tangent-slide: remove this surface's normal component of the\ndisplacement ``coords - reference`` (the projected P1 normal is taken at\n``reference``). Nodes with a degenerate normal keep ``reference``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "BoundingSurface", + "is_public": true + }, + { + "name": "restore", + "kind": "method", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 161, + "signature": "(self, coords)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Snap ``coords`` back onto this surface (kind-specific).\n\n``radial`` \u2014 re-impose ``|r| = radius`` about ``centre`` (exact,\nconcave-safe). ``plane`` \u2014 orthogonal projection onto the plane.\n``facet`` \u2014 nearest point on the surface's reference facets (segments\nin 2D, triangles in 3D); convex-safe, with a documented concave bias.\nA ``free``/``is_free`` surface returns ``coords`` unchanged (it follows\nthe live discrete surface \u2014 a follow-up).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "BoundingSurface", + "is_public": true + }, + { + "name": "release", + "kind": "method", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 193, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Flip a rigid (``radial``/``plane``) surface to ``free``: subsequent\n:meth:`restore` follows the live discrete surface instead of the analytic\ntarget. Records the previous kind on ``self._prev_kind``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "BoundingSurface", + "is_public": true + }, + { + "name": "register_radial_surfaces", + "kind": "function", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 213, + "signature": "(mesh, centre, label_radius)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "centre", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "label_radius", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Register ``radial`` surfaces: ``label_radius = {label_name: radius}``.\n\nCalled by analytic radial-boundary constructors (Annulus, SphericalShell,\nCubedSphere). ``centre`` is the common centre (e.g. the origin).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "register_plane_surfaces", + "kind": "function", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 224, + "signature": "(mesh, label_plane)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "label_plane", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Register ``plane`` surfaces: ``label_plane = {label_name: (point, normal)}``.\n\nCalled by box constructors for axis-aligned (or general) faces.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "register_box_face_surfaces", + "kind": "function", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 233, + "signature": "(mesh, minCoords, maxCoords)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "minCoords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "maxCoords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Register ``plane`` surfaces for an axis-aligned box's faces.\n\nMatches the box constructors' labels:\n2D \u2014 ``Left``/``Right`` (``x = x_min/max``), ``Bottom``/``Top``\n(``y = y_min/max``); 3D \u2014 ``Left``/``Right`` (``x``), ``Front``/``Back``\n(``y``), ``Bottom``/``Top`` (``z``). Box corners/edges are junctions of two\nfaces and are pinned by the slip orchestrator (the normal is ambiguous).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "UnstructuredSimplexBox", + "kind": "function", + "file": "src/underworld3/meshing/cartesian.py", + "line": 33, + "signature": "(minCoords: Tuple = (0.0, 0.0), maxCoords: Tuple = (1.0, 1.0), cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, regular: bool = False, filename = None, refinement = None, gmsh_verbosity = 0, units = None, verbose = False)", + "parameters": [ + { + "name": "minCoords", + "type_hint": "Tuple", + "default": "(0.0, 0.0)", + "description": "" + }, + { + "name": "maxCoords", + "type_hint": "Tuple", + "default": "(1.0, 1.0)", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "regular", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create an unstructured simplex mesh on a rectangular box domain.\n\nGenerates a triangular (2D) or tetrahedral (3D) mesh using Gmsh,\nwith named boundary labels for applying boundary conditions.\n\nParameters\n----------\nminCoords : tuple of float\n Minimum corner coordinates ``(x_min, y_min)`` for 2D or\n ``(x_min, y_min, z_min)`` for 3D. Supports plain numbers\n (model units) or UWQuantity objects (auto-converted).\nmaxCoords : tuple of float\n Maximum corner coordinates ``(x_max, y_max)`` for 2D or\n ``(x_max, y_max, z_max)`` for 3D. Supports plain numbers\n or UWQuantity objects.\ncellSize : float\n Target mesh element size. Controls mesh density; smaller\n values produce finer meshes with more elements.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\n Use ``degree=1`` for linear elements, ``degree=2`` for quadratic.\nqdegree : int, default=2\n Quadrature degree for numerical integration. Should typically\n be at least ``2 * degree`` for accuracy.\nregular : bool, default=False\n If True, use transfinite meshing for a more structured layout.\n Currently only works for 2D meshes.\nfilename : str, optional\n Path to save the mesh file. If None, generates a unique name\n in the ``.meshes/`` directory based on mesh parameters.\nrefinement : int, optional\n Number of uniform refinement levels to apply after mesh\n generation. Each level approximately quadruples element count.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level. 0 is silent, higher values\n produce more diagnostic output.\nunits : str, optional\n Deprecated and ignored (DeprecationWarning). Mesh coordinates are\n always in the model's units (``model.set_reference_quantities``).\nverbose : bool, default=False\n If True, print additional diagnostic information during\n mesh construction.\n\nReturns\n-------\nMesh\n An Underworld mesh object with the following boundaries defined:\n\n **2D boundaries** (accessible via ``mesh.boundaries``):\n\n - ``Bottom``: :math:`y = y_{min}` edge\n - ``Top``: :math:`y = y_{max}` edge\n - ``Right``: :math:`x = x_{max}` edge\n - ``Left``: :math:`x = x_{min}` edge\n\n **3D boundaries**:\n\n - ``Bottom``: :math:`z = z_{min}` face\n - ``Top``: :math:`z = z_{max}` face\n - ``Right``: :math:`x = x_{max}` face\n - ``Left``: :math:`x = x_{min}` face\n - ``Front``: :math:`y = y_{min}` face\n - ``Back``: :math:`y = y_{max}` face\n\nSee Also\n--------\nStructuredQuadBox : For quadrilateral/hexahedral meshes.\nBoxInternalBoundary : For box meshes with an internal interface.\n\nExamples\n--------\nCreate a 2D unit square mesh:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(\n... minCoords=(0.0, 0.0),\n... maxCoords=(1.0, 1.0),\n... cellSize=0.1\n... )\n>>> mesh.dim\n2\n\nCreate a 3D box with finer resolution:\n\n>>> mesh3d = uw.meshing.UnstructuredSimplexBox(\n... minCoords=(0.0, 0.0, 0.0),\n... maxCoords=(2.0, 1.0, 1.0),\n... cellSize=0.05,\n... degree=2\n... )\n\nAccess boundary labels for boundary conditions:\n\n>>> mesh.boundaries.Bottom\n\n\nNotes\n-----\nMesh coordinates are always in non-dimensional (scaled) units (set via\n``uw.scaling.get_coefficients()``). If UWQuantity objects with\nphysical units are passed, they are automatically converted using\n``uw.scaling.non_dimensionalise()``.\n\nThe ``regular=True`` option produces a more structured mesh layout\nbut currently only works for 2D meshes.", + "harvested_comments": [ + "Enum is not quite natural but matches the above", + "Convert coordinates to non-dimensional units (handles UWQuantity objects)", + "Add Physical groups for boundaries", + "Add Physical groups", + "Generate Mesh" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "BoxInternalBoundary", + "kind": "function", + "file": "src/underworld3/meshing/cartesian.py", + "line": 365, + "signature": "(elementRes: Optional[Tuple[int, int, int]] = (8, 8, 8), zelementRes: Optional[Tuple[int, int]] = (4, 4), cellSize: float = 0.1, minCoords: Optional[Tuple[float, float, float]] = (0, 0, 0), maxCoords: Optional[Tuple[float, float, float]] = (1, 1, 1), zintCoord: float = 0.5, simplex: bool = False, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, units = None, verbose = False)", + "parameters": [ + { + "name": "elementRes", + "type_hint": "Optional[Tuple[int, int, int]]", + "default": "(8, 8, 8)", + "description": "" + }, + { + "name": "zelementRes", + "type_hint": "Optional[Tuple[int, int]]", + "default": "(4, 4)", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "minCoords", + "type_hint": "Optional[Tuple[float, float, float]]", + "default": "(0, 0, 0)", + "description": "" + }, + { + "name": "maxCoords", + "type_hint": "Optional[Tuple[float, float, float]]", + "default": "(1, 1, 1)", + "description": "" + }, + { + "name": "zintCoord", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "simplex", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a box mesh with an internal horizontal boundary.\n\nGenerates a 2D or 3D mesh with an embedded internal boundary surface,\nuseful for problems with material interfaces, phase boundaries, or\nlayered domains that require flux calculations across the interface.\n\nParameters\n----------\nelementRes : tuple of int, default=(8, 8, 8)\n Number of elements in each direction ``(nx, ny)`` for 2D or\n ``(nx, ny, nz)`` for 3D. Used when ``simplex=False`` (structured).\nzelementRes : tuple of int, default=(4, 4)\n Number of elements ``(n_below, n_above)`` in the vertical direction\n below and above the internal boundary. Allows different resolution\n in each layer.\ncellSize : float, default=0.1\n Target element size for unstructured meshing (``simplex=True``).\n Ignored for structured meshes.\nminCoords : tuple of float, default=(0, 0, 0)\n Minimum corner coordinates. Length determines dimensionality:\n 2-tuple for 2D, 3-tuple for 3D.\nmaxCoords : tuple of float, default=(1, 1, 1)\n Maximum corner coordinates.\nzintCoord : float, default=0.5\n Vertical coordinate of the internal boundary surface.\n In 2D this is the y-coordinate; in 3D the z-coordinate.\nsimplex : bool, default=False\n If False, create a structured quadrilateral/hexahedral mesh.\n If True, create an unstructured triangular/tetrahedral mesh.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file. If None, auto-generates in ``.meshes/``.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nunits : str, optional\n Deprecated and ignored (DeprecationWarning). Mesh coordinates are\n always in the model's units (``model.set_reference_quantities``).\nverbose : bool, default=False\n Print diagnostic information during mesh construction.\n\nReturns\n-------\nMesh\n An Underworld mesh object with boundaries including an internal\n interface:\n\n **2D boundaries**:\n\n - ``Bottom``: :math:`y = y_{min}` edge\n - ``Top``: :math:`y = y_{max}` edge\n - ``Right``: :math:`x = x_{max}` edge\n - ``Left``: :math:`x = x_{min}` edge\n - ``Internal``: :math:`y = z_{int}` interface\n\n **3D boundaries**:\n\n - ``Bottom``: :math:`z = z_{min}` face\n - ``Top``: :math:`z = z_{max}` face\n - ``Right``: :math:`x = x_{max}` face\n - ``Left``: :math:`x = x_{min}` face\n - ``Front``: :math:`y = y_{min}` face\n - ``Back``: :math:`y = y_{max}` face\n - ``Internal``: :math:`z = z_{int}` interface\n\nSee Also\n--------\nUnstructuredSimplexBox : Box mesh without internal boundary.\nAnnulusInternalBoundary : Annular mesh with internal boundary.\n\nExamples\n--------\nCreate a 2D layered domain with an interface at y=0.5:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.BoxInternalBoundary(\n... minCoords=(0.0, 0.0),\n... maxCoords=(1.0, 1.0),\n... zintCoord=0.5,\n... elementRes=(16, 16),\n... zelementRes=(8, 8)\n... )\n\nAccess the internal boundary for flux calculations:\n\n>>> mesh.boundaries.Internal\n\n\nNotes\n-----\nThe internal boundary is useful for:\n\n- Calculating heat flux across a thermal boundary layer\n- Tracking mass flux between mantle and crust\n- Applying different material properties in each layer", + "harvested_comments": [ + "Below internal boundary", + "Above internal boundary", + "Below internal boundary", + "Above internal boundary", + "Every rank passes these Enums to Mesh() below, so they must be bound" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "StructuredQuadBox", + "kind": "function", + "file": "src/underworld3/meshing/cartesian.py", + "line": 967, + "signature": "(elementRes: Optional[Tuple[int, int, int]] = (16, 16), minCoords: Optional[Tuple[float, float, float]] = None, maxCoords: Optional[Tuple[float, float, float]] = None, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, units = None, verbose = False)", + "parameters": [ + { + "name": "elementRes", + "type_hint": "Optional[Tuple[int, int, int]]", + "default": "(16, 16)", + "description": "" + }, + { + "name": "minCoords", + "type_hint": "Optional[Tuple[float, float, float]]", + "default": "None", + "description": "" + }, + { + "name": "maxCoords", + "type_hint": "Optional[Tuple[float, float, float]]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a structured quadrilateral or hexahedral box mesh.\n\nGenerates a mesh with regular rectangular (2D) or brick (3D) elements\nusing transfinite meshing. Provides precise control over element count\nin each direction.\n\nParameters\n----------\nelementRes : tuple of int, default=(16, 16)\n Number of elements in each direction. Use ``(nx, ny)`` for 2D\n or ``(nx, ny, nz)`` for 3D. This tuple also determines the\n mesh dimensionality.\nminCoords : tuple of float, optional\n Minimum corner coordinates. Defaults to ``(0.0, 0.0)`` for 2D\n or ``(0.0, 0.0, 0.0)`` for 3D based on ``elementRes`` length.\n Supports plain numbers or UWQuantity objects.\nmaxCoords : tuple of float, optional\n Maximum corner coordinates. Defaults to ``(1.0, 1.0)`` for 2D\n or ``(1.0, 1.0, 1.0)`` for 3D. Supports UWQuantity objects.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\n Use ``degree=1`` for bilinear/trilinear elements,\n ``degree=2`` for biquadratic/triquadratic.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file. If None, auto-generates in ``.meshes/``.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nunits : str, optional\n Deprecated and ignored (DeprecationWarning). Mesh coordinates are\n always in the model's units (``model.set_reference_quantities``).\nverbose : bool, default=False\n Print diagnostic information during mesh construction.\n\nReturns\n-------\nMesh\n An Underworld mesh object with structured elements and boundaries:\n\n **2D boundaries** (same as UnstructuredSimplexBox):\n\n - ``Bottom``: :math:`y = y_{min}` edge\n - ``Top``: :math:`y = y_{max}` edge\n - ``Right``: :math:`x = x_{max}` edge\n - ``Left``: :math:`x = x_{min}` edge\n\n **3D boundaries**:\n\n - ``Bottom``: :math:`z = z_{min}` face\n - ``Top``: :math:`z = z_{max}` face\n - ``Right``: :math:`x = x_{max}` face\n - ``Left``: :math:`x = x_{min}` face\n - ``Front``: :math:`y = y_{min}` face\n - ``Back``: :math:`y = y_{max}` face\n\nSee Also\n--------\nUnstructuredSimplexBox : For triangular/tetrahedral meshes.\nBoxInternalBoundary : For box meshes with an internal interface.\n\nExamples\n--------\nCreate a 2D structured mesh with 32x32 elements:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.StructuredQuadBox(\n... elementRes=(32, 32),\n... minCoords=(0.0, 0.0),\n... maxCoords=(1.0, 1.0)\n... )\n\nCreate a 3D mesh (note the 3-element tuple):\n\n>>> mesh3d = uw.meshing.StructuredQuadBox(\n... elementRes=(16, 16, 8),\n... maxCoords=(2.0, 2.0, 1.0)\n... )\n\nNotes\n-----\nStructured meshes have predictable element layouts which can be\nadvantageous for:\n\n- Consistent interpolation behaviour\n- Benchmark problems with known analytical solutions\n- Simpler mesh-to-mesh comparisons in convergence studies\n\nThe mesh dimensionality is determined by the length of ``elementRes``:\n2-tuple creates a 2D mesh, 3-tuple creates a 3D mesh.", + "harvested_comments": [ + "boundaries = {\"Bottom\": 1, \"Top\": 2, \"Right\": 3, \"Left\": 4, \"Front\": 5, \"Back\": 6}", + "Enum is not quite natural but matches the above", + "Convert coordinates to non-dimensional units (handles UWQuantity objects)", + "Create Box Geometry", + "Add Physical groups for boundaries" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "points", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 114, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(N, 3) array of surface points.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "points", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 119, + "signature": "(self, value: np.ndarray)", + "parameters": [ + { + "name": "value", + "type_hint": "np.ndarray", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set points and invalidate cached data.", + "harvested_comments": [ + "Invalidate derived data" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "triangles", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 135, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(M, 3) array of triangle vertex indices.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "normals", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 140, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(M, 3) array of face normals.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "pv_mesh", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 145, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "PyVista PolyData mesh (None if not triangulated or pyvista unavailable).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "is_triangulated", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 150, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Whether the surface has been triangulated.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "n_points", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 155, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Number of points in the surface.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "n_triangles", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 160, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Number of triangles in the surface.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "from_vtk", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 165, + "signature": "(cls, filename: str, name: str = None) -> 'FaultSurface'", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "'FaultSurface'", + "existing_docstring": "Load fault surface from VTK file.\n\nArgs:\n filename: Path to VTK file (.vtk or .vtp)\n name: Name for the fault. If None, uses filename stem.\n\nReturns:\n FaultSurface: Loaded fault surface with triangulation and normals\n\nRaises:\n FileNotFoundError: If file doesn't exist\n ImportError: If pyvista not available", + "harvested_comments": [ + "Load the VTK file", + "Extract triangles from faces", + "VTK faces format: [n_verts, v0, v1, v2, n_verts, v0, v1, v2, ...]", + "Reshape to extract triangles (assumes all triangles)", + "Extract or compute normals" + ], + "status": "complete", + "needs": [], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "triangulate", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 213, + "signature": "(self, offset: float = 0.01) -> None", + "parameters": [ + { + "name": "offset", + "type_hint": "float", + "default": "0.01", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Triangulate point cloud using pyvista delaunay_2d.\n\nThis creates a triangulated surface from the point cloud by projecting\npoints onto a best-fit plane, performing 2D Delaunay triangulation,\nand mapping back to 3D.\n\nArgs:\n offset: Height offset for delaunay_2d (controls curvature tolerance).\n Larger values allow more curved surfaces.\n\nRaises:\n ImportError: If pyvista not available\n ValueError: If points too sparse for triangulation (< 3 points)\n RuntimeError: If triangulation fails", + "harvested_comments": [ + "Check for degenerate cases (all points nearly collinear)", + "Compute bounding box extent", + "If smallest extent is negligible compared to largest, points may be collinear", + "Create PolyData from points and triangulate", + "This runs on all ranks redundantly (pyvista doesn't work in parallel)" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "compute_normals", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 271, + "signature": "(self, consistent_normals: bool = True) -> None", + "parameters": [ + { + "name": "consistent_normals", + "type_hint": "bool", + "default": "True", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Recompute face normals for triangulated surface.\n\nArgs:\n consistent_normals: If True, attempt to make normals consistently oriented", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "flip_normals", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 290, + "signature": "(self) -> None", + "parameters": [], + "returns": "None", + "existing_docstring": "Flip the direction of all face normals.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "to_vtk", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 297, + "signature": "(self, filename: str) -> None", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Export triangulated surface to VTK file.\n\nArgs:\n filename: Output path (.vtk or .vtp)\n\nRaises:\n RuntimeError: If surface not triangulated\n ImportError: If pyvista not available", + "harvested_comments": [ + "Ensure we have a pyvista mesh", + "Reconstruct from arrays" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "build_kdtree", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 326, + "signature": "(self) -> 'uw.kdtree.KDTree'", + "parameters": [], + "returns": "'uw.kdtree.KDTree'", + "existing_docstring": "Build KDTree for nearest-neighbor queries on face centers.\n\nReturns:\n KDTree built from triangle centroids\n\nRaises:\n RuntimeError: If surface not triangulated", + "harvested_comments": [ + "Compute triangle centroids" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "face_centers", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 348, + "signature": "(self) -> np.ndarray", + "parameters": [], + "returns": "np.ndarray", + "existing_docstring": "(M, 3) array of triangle centroids.", + "harvested_comments": [ + "Compute manually from triangles" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultSurface", + "is_public": true + }, + { + "name": "add", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 405, + "signature": "(self, fault: FaultSurface) -> None", + "parameters": [ + { + "name": "fault", + "type_hint": "FaultSurface", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Add a fault surface to the collection.\n\nArgs:\n fault: FaultSurface to add\n\nRaises:\n ValueError: If fault with same name already exists", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "add_from_vtk", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 421, + "signature": "(self, filename: str, name: str = None) -> FaultSurface", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "FaultSurface", + "existing_docstring": "Load and add a fault from VTK file.\n\nArgs:\n filename: Path to VTK file\n name: Name for the fault. If None, uses filename stem.\n\nReturns:\n The loaded FaultSurface", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "remove", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 435, + "signature": "(self, name: str) -> FaultSurface", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "FaultSurface", + "existing_docstring": "Remove and return a fault from the collection.\n\nArgs:\n name: Name of fault to remove\n\nReturns:\n The removed FaultSurface\n\nRaises:\n KeyError: If fault not found", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "names", + "kind": "property", + "file": "src/underworld3/meshing/faults.py", + "line": 462, + "signature": "(self) -> List[str]", + "parameters": [], + "returns": "List[str]", + "existing_docstring": "List of fault names.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "compute_distance_field", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 466, + "signature": "(self, mesh: 'Mesh', distance_var: 'MeshVariable' = None, variable_name: str = 'fault_distance') -> 'MeshVariable'", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "distance_var", + "type_hint": "'MeshVariable'", + "default": "None", + "description": "" + }, + { + "name": "variable_name", + "type_hint": "str", + "default": "'fault_distance'", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Compute minimum distance from mesh points to any fault surface.\n\nUses pyvista's compute_implicit_distance for accurate signed distance\ncomputation. The returned field contains the absolute distance to the\nnearest fault surface at each mesh point.\n\nParameters\n----------\nmesh : Mesh\n The mesh to compute distances on.\ndistance_var : MeshVariable, optional\n Existing MeshVariable to store results.\n If None, creates a new variable.\nvariable_name : str, optional\n Name for new variable if distance_var is None.\n\nReturns\n-------\nMeshVariable\n Scalar variable with distance values (1 component).\n\nRaises\n------\nValueError\n If collection is empty or no faults are triangulated.\nImportError\n If pyvista is not available.", + "harvested_comments": [ + "Check all faults are triangulated", + "Create or validate output variable", + "Get mesh coordinates and create pyvista point cloud", + "(avoids visualisation module which initializes trame)", + "Initialize with large distance" + ], + "status": "complete", + "needs": [], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "transfer_normals", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 542, + "signature": "(self, mesh: 'Mesh', coords: np.ndarray = None, normal_var: 'MeshVariable' = None, variable_name: str = 'fault_normals') -> 'MeshVariable'", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": "np.ndarray", + "default": "None", + "description": "" + }, + { + "name": "normal_var", + "type_hint": "'MeshVariable'", + "default": "None", + "description": "" + }, + { + "name": "variable_name", + "type_hint": "str", + "default": "'fault_normals'", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Transfer fault normals to mesh points via nearest-neighbor lookup.\n\nFor each mesh point, finds the closest fault face (from any fault in\nthe collection) and copies that face's normal vector.\n\nParameters\n----------\nmesh : Mesh\n The mesh to transfer normals to.\ncoords : ndarray, optional\n Coordinates to query. If None, uses mesh.X.coords.\nnormal_var : MeshVariable, optional\n Existing MeshVariable to store results.\n If None, creates a new variable.\nvariable_name : str, optional\n Name for new variable if normal_var is None.\n\nReturns\n-------\nMeshVariable\n Variable with normal vectors (3 components).\n\nRaises\n------\nValueError\n If collection is empty or no faults are triangulated.", + "harvested_comments": [ + "Check all faults are triangulated", + "Get query coordinates", + "Handle UnitAwareArray by extracting raw values", + "Create or validate output variable", + "Build combined arrays of all fault face centers and normals" + ], + "status": "complete", + "needs": [], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "create_weakness_function", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 622, + "signature": "(self, distance_var: 'MeshVariable', fault_width: float, eta_weak: float = 0.01, eta_background: float = 1.0) -> sympy.Expr", + "parameters": [ + { + "name": "distance_var", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "fault_width", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "eta_weak", + "type_hint": "float", + "default": "0.01", + "description": "" + }, + { + "name": "eta_background", + "type_hint": "float", + "default": "1.0", + "description": "" + } + ], + "returns": "sympy.Expr", + "existing_docstring": "Create Piecewise viscosity function for fault weakness.\n\nCreates a sympy Piecewise expression that gives:\n- eta_weak when distance < fault_width\n- eta_background otherwise\n\nThis can be used directly with TransverseIsotropicFlowModel.Parameters.eta_1\nfor creating anisotropic weakness along fault zones.\n\nArgs:\n distance_var: MeshVariable containing fault distances\n fault_width: Width of the weak zone around faults\n eta_weak: Viscosity within fault zone (default 0.01)\n eta_background: Viscosity outside fault zone (default 1.0)\n\nReturns:\n sympy.Piecewise expression for use in constitutive models\n\nExample:\n >>> eta_1 = faults.create_weakness_function(\n ... fault_distance,\n ... fault_width=mesh.get_min_radius() * 5,\n ... eta_weak=0.01,\n ... )\n >>> stokes.constitutive_model.Parameters.eta_1 = eta_1", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "FaultCollection", + "is_public": true + }, + { + "name": "RegionalSphericalBox", + "kind": "function", + "file": "src/underworld3/meshing/geographic.py", + "line": 29, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, SWcorner = [-45, -45], NEcorner = [+45, +45], numElementsLon: int = 5, numElementsLat: int = 5, numElementsDepth: int = 5, degree: int = 1, qdegree: int = 2, simplex: bool = False, filename = None, refinement = None, coarsening = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "SWcorner", + "type_hint": null, + "default": "[-45, -45]", + "description": "" + }, + { + "name": "NEcorner", + "type_hint": null, + "default": "[+45, +45]", + "description": "" + }, + { + "name": "numElementsLon", + "type_hint": "int", + "default": "5", + "description": "" + }, + { + "name": "numElementsLat", + "type_hint": "int", + "default": "5", + "description": "" + }, + { + "name": "numElementsDepth", + "type_hint": "int", + "default": "5", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "simplex", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "coarsening", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a regional spherical box mesh (cubed-sphere section).\n\nGenerates a 3D structured mesh for a regional section of a spherical shell,\nusing a cubed-sphere projection. The domain is defined by corner coordinates\nin degrees (longitude, latitude) and radial bounds.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the spherical shell.\nradiusInner : float, default=0.547\n Inner radius of the spherical shell.\nSWcorner : list of float, default=[-45, -45]\n Southwest corner as [longitude, latitude] in degrees.\nNEcorner : list of float, default=[+45, +45]\n Northeast corner as [longitude, latitude] in degrees.\nnumElementsLon : int, default=5\n Number of elements in the longitude direction.\nnumElementsLat : int, default=5\n Number of elements in the latitude direction.\nnumElementsDepth : int, default=5\n Number of elements in the radial (depth) direction.\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nsimplex : bool, default=False\n If True, use tetrahedral elements; if False, use hexahedral.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ncoarsening : int, optional\n Number of coarsening levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n - ``North``: Northern boundary at :math:`\\phi = \\phi_{max}`\n - ``South``: Southern boundary at :math:`\\phi = \\phi_{min}`\n - ``East``: Eastern boundary at :math:`\\lambda = \\lambda_{max}`\n - ``West``: Western boundary at :math:`\\lambda = \\lambda_{min}`\n\n The mesh uses a SPHERICAL coordinate system and includes a refinement\n callback that snaps boundary nodes to true spherical geometry.\n\nSee Also\n--------\nCubedSphere : Full cubed-sphere mesh.\nRegionalGeographicBox : Geographic mesh with ellipsoidal geometry.\nSphericalShell : Unstructured spherical shell.\n\nExamples\n--------\nCreate a regional mesh for the Australian region:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.RegionalSphericalBox(\n... radiusOuter=1.0,\n... radiusInner=0.9,\n... SWcorner=[110, -45],\n... NEcorner=[155, -10],\n... numElementsLon=10,\n... numElementsLat=8,\n... numElementsDepth=5\n... )\n\nNotes\n-----\nThis mesh uses a cubed-sphere projection, which provides more uniform\nelement sizes than a latitude-longitude grid. The structured mesh is\nsuitable for regional mantle convection models where boundary-aligned\nelements are beneficial.\n\nThe coordinate system provides unit vectors via ``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "harvested_comments": [ + "lat_south = np.radians(centralLatitude - latitudeExtent/2)", + "lat_north = np.radians(centralLatitude + latitudeExtent/2)", + "ss = min(longitudeExtent / 90, 1.99) * np.cos(lat_south)/np.cos(np.pi/4)", + "sn = min(longitudeExtent / 90, 1.99) * np.cos(lat_north)/np.cos(np.pi/4)", + "t = min(latitudeExtent / 90, 1.99)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "RegionalGeographicBox", + "kind": "function", + "file": "src/underworld3/meshing/geographic.py", + "line": 394, + "signature": "(lon_range: Tuple[float, float] = (135.0, 140.0), lat_range: Tuple[float, float] = (-35.0, -30.0), depth_range: Tuple[float, float] = (0.0, 400.0), ellipsoid = 'WGS84', numElements: Tuple[int, int, int] = (10, 10, 10), degree: int = 1, qdegree: int = 2, simplex: bool = True, filename: Optional[str] = None, refinement: Optional[int] = None, coarsening: Optional[int] = None, gmsh_verbosity: int = 0, verbose: bool = False)", + "parameters": [ + { + "name": "lon_range", + "type_hint": "Tuple[float, float]", + "default": "(135.0, 140.0)", + "description": "" + }, + { + "name": "lat_range", + "type_hint": "Tuple[float, float]", + "default": "(-35.0, -30.0)", + "description": "" + }, + { + "name": "depth_range", + "type_hint": "Tuple[float, float]", + "default": "(0.0, 400.0)", + "description": "" + }, + { + "name": "ellipsoid", + "type_hint": null, + "default": "'WGS84'", + "description": "" + }, + { + "name": "numElements", + "type_hint": "Tuple[int, int, int]", + "default": "(10, 10, 10)", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "simplex", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "filename", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": "Optional[int]", + "default": "None", + "description": "" + }, + { + "name": "coarsening", + "type_hint": "Optional[int]", + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": "int", + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a regional geographic mesh with ellipsoidal geometry.\n\nThis function creates a structured 3D mesh in geographic coordinates\n(longitude, latitude, depth) on an ellipsoidal planet. The mesh uses\ngeodetic latitude (perpendicular to ellipsoid surface) and measures\ndepth below the reference ellipsoid surface.\n\nParameters\n----------\nlon_range : tuple of float, optional\n Longitude range in degrees East (lon_min, lon_max).\n Default: (135.0, 140.0) for southeastern Australia.\nlat_range : tuple of float, optional\n Latitude range in degrees North (lat_min, lat_max).\n Geodetic latitude (perpendicular to ellipsoid).\n Default: (-35.0, -30.0) for southeastern Australia.\ndepth_range : tuple of float, optional\n Depth range in km below ellipsoid surface (depth_min, depth_max).\n Positive downward. depth_min=0 means surface.\n Default: (0.0, 400.0) for 0-400 km depth.\nellipsoid : str, tuple, or bool, optional\n Ellipsoid specification:\n - str: Name from ELLIPSOIDS dict ('WGS84', 'Mars', 'Moon', 'Venus', 'sphere')\n - tuple: (semi_major_axis_km, semi_minor_axis_km) for custom ellipsoid\n - True: Use WGS84 (default)\n - False or 'sphere': Use perfect sphere with Earth mean radius\n Default: 'WGS84'\nnumElements : tuple of int, optional\n Number of elements in (lon, lat, depth) directions.\n Default: (10, 10, 10)\ndegree : int, optional\n Polynomial degree for finite elements (1=linear, 2=quadratic).\n Default: 1\nqdegree : int, optional\n Quadrature degree for numerical integration.\n Default: 2\nsimplex : bool, optional\n If True, use tetrahedral elements. If False, use hexahedral elements.\n Default: True\nfilename : str, optional\n Path to save generated mesh file. If None, uses automatic naming.\n Default: None\nrefinement : int, optional\n Number of uniform refinement steps to apply.\n Default: None\ncoarsening : int, optional\n Number of coarsening steps to apply.\n Default: None\ngmsh_verbosity : int, optional\n Gmsh output verbosity level (0=quiet, 5=very verbose).\n Default: 0\nverbose : bool, optional\n If True, print mesh generation details.\n Default: False\n\nReturns\n-------\nMesh\n Underworld3 mesh object with GEOGRAPHIC coordinate system.\n Access geographic coordinates via mesh.geo:\n - mesh.geo.lon, mesh.geo.lat, mesh.geo.depth (data arrays)\n - mesh.geo[:] for symbolic coordinates (\u03bb_lon, \u03bb_lat, \u03bb_d)\n - mesh.geo.unit_east, mesh.geo.unit_north, mesh.geo.unit_down (basis vectors)\n\nExamples\n--------\nCreate mesh for southeastern Australia, 0-400 km depth::\n\n mesh = uw.meshing.RegionalGeographicBox(\n lon_range=(135, 140),\n lat_range=(-35, -30),\n depth_range=(0, 400),\n ellipsoid='WGS84',\n numElements=(20, 20, 10),\n )\n\nAccess geographic coordinates::\n\n lon = mesh.geo.lon\n lat = mesh.geo.lat\n depth = mesh.geo.depth\n\nNotes\n-----\nUses geodetic latitude (GPS/map standard), not geocentric latitude.\nDepth is measured from reference ellipsoid surface, not from center.\n``mesh.R`` provides spherical coordinates for backward compatibility.\n``mesh.geo`` provides geographic coordinates with ellipsoid geometry.\nRight-handed coordinate system: WE x SN = down.", + "harvested_comments": [ + "Check if units system is active", + "Process input parameters with unit awareness", + "Angles: accept quantities or raw (degrees)", + "Depths: require quantities when units active", + "Parse ellipsoid parameter" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SegmentedSphericalSurface2D", + "kind": "function", + "file": "src/underworld3/meshing/segmented.py", + "line": 30, + "signature": "(radius: float = 1.0, cellSize: float = 0.05, numSegments: int = 6, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radius", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.05", + "description": "" + }, + { + "name": "numSegments", + "type_hint": "int", + "default": "6", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a 2D spherical surface mesh using orange-peel segmentation.\n\nGenerates a mesh on the surface of a sphere by dividing it into\nlongitudinal segments (like an orange peel), with coordinates\nconverted to :math:`(\\lambda, \\phi)` (longitude, latitude).\n\nParameters\n----------\nradius : float, default=1.0\n Radius of the sphere.\ncellSize : float, default=0.05\n Target mesh element size.\nnumSegments : int, default=6\n Number of longitudinal segments (orange-peel divisions).\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D surface mesh with point boundaries:\n\n - ``NPole``: North pole point\n - ``SPole``: South pole point\n - ``Poles``: Both poles combined\n\n Uses SPHERE_SURFACE_NATIVE coordinate system with\n :math:`(\\lambda, \\phi)` coordinates.\n\nSee Also\n--------\nSegmentedSphericalShell : 3D segmented spherical shell.\nSphericalShell : Standard unstructured spherical shell.\n\nWarnings\n--------\nThis mesh uses PETSc periodicity features that may be unreliable.\nConsider using alternative mesh types for production work.\n\nNotes\n-----\nThe mesh is constructed by creating curved triangular segments from\npole to pole, then converting the Cartesian coordinates to longitude-\nlatitude representation. Periodicity is set up to handle the :math:`\\pm\\pi`\nlongitude wrap-around.", + "harvested_comments": [ + "Mesh like an orange", + "Curve loops:", + "Add some physical labels etc.", + "Generate Mesh", + "xyz coordinates of the mesh" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SegmentedSphericalShell", + "kind": "function", + "file": "src/underworld3/meshing/segmented.py", + "line": 238, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, numSegments: int = 6, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, coordinatesNative = False, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "numSegments", + "type_hint": "int", + "default": "6", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "coordinatesNative", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a 3D spherical shell mesh using orange-peel segmentation.\n\nGenerates a spherical shell mesh by dividing the sphere into\nlongitudinal wedge segments, similar to an orange peel. Each segment\nis a 3D wedge extending from the inner to outer radius.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the shell.\nradiusInner : float, default=0.547\n Inner radius of the shell.\ncellSize : float, default=0.1\n Target mesh element size.\nnumSegments : int, default=6\n Number of longitudinal segments.\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ncoordinatesNative : bool, default=False\n If True, use native :math:`(r, \\theta, \\phi)` coordinates\n with periodicity in :math:`\\phi`. If False, use Cartesian\n coordinates with spherical coordinate system overlay.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``LowerPlus``: Inner surface + slice boundaries\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n - ``UpperPlus``: Outer surface + slice boundaries\n - ``Slices``: Radial slice boundaries between segments\n\n The mesh provides boundary normals for free-slip conditions\n on the radial boundaries.\n\nSee Also\n--------\nSegmentedSphericalBall : Solid ball version (no inner boundary).\nSphericalShell : Standard unstructured spherical shell.\nCubedSphere : Alternative structured spherical mesh.\n\nWarnings\n--------\nWhen ``coordinatesNative=True``, this mesh uses PETSc periodicity\nfeatures that may be unreliable. Consider using ``coordinatesNative=False``\nor alternative mesh types for production work.\n\nNotes\n-----\nThe ``LowerPlus`` and ``UpperPlus`` boundaries combine the radial\nsurfaces with the slice boundaries, useful for applying no-slip\nconditions that include the radial walls.\n\nThe coordinate system provides unit vectors via ``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "harvested_comments": [ + "# Follow the lead of the cubed sphere and make copies of a segment", + "Make boundaries", + "Make surfaces", + "Make volume", + "Make copies" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SegmentedSphericalBall", + "kind": "function", + "file": "src/underworld3/meshing/segmented.py", + "line": 691, + "signature": "(radius: float = 1.0, cellSize: float = 0.1, numSegments: int = 6, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, coordinatesNative = False, verbosity = 0, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radius", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "numSegments", + "type_hint": "int", + "default": "6", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "coordinatesNative", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a 3D solid spherical ball mesh using orange-peel segmentation.\n\nGenerates a solid sphere mesh (no inner cavity) by dividing it into\nlongitudinal wedge segments extending from the centre to the surface.\n\nParameters\n----------\nradius : float, default=1.0\n Radius of the sphere.\ncellSize : float, default=0.1\n Target mesh element size.\nnumSegments : int, default=6\n Number of longitudinal segments.\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ncoordinatesNative : bool, default=False\n If True, use native :math:`(r, \\theta, \\phi)` coordinates\n with periodicity in :math:`\\phi`. If False, use Cartesian\n coordinates with spherical coordinate system overlay.\nverbosity : int, default=0\n Deprecated. Use ``verbose`` instead.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Upper``: Outer surface at :math:`r = radius`\n - ``UpperPlus``: Outer surface + slice boundaries\n - ``Centre``: Central point\n - ``Slices``: Radial slice boundaries between segments\n\n The mesh provides boundary normals for free-slip conditions\n on the outer surface.\n\nSee Also\n--------\nSegmentedSphericalShell : Shell version with inner boundary.\nSphericalShell : Standard unstructured spherical shell.\n\nWarnings\n--------\nWhen ``coordinatesNative=True``, this mesh uses PETSc periodicity\nfeatures that may be unreliable. Consider using ``coordinatesNative=False``\nor alternative mesh types for production work.\n\nNotes\n-----\nThis mesh is useful for modelling solid bodies (e.g., planetary cores,\nasteroids) where elements must extend to the centre. The segmented\napproach avoids the pole singularity issues of latitude-longitude grids.\n\nThe coordinate system provides unit vectors via ``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "harvested_comments": [ + "# Follow the lead of the cubed sphere and make copies of a segment", + "Make boundaries", + "faceLoopi = gmsh.model.geo.addCurveLoop(", + "[edgeWi, edgeEqi, edgeEi], tag=-1, reorient=True", + "Make surfaces" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "mesh_metric_mismatch", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 360, + "signature": "(mesh, metric, resolution_ratio = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "metric", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "resolution_ratio", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Geometric mismatch between the current mesh and what the\nequidistribution rule would prescribe from ``metric``.\n\nPer cell compute the equidistribution-prescribed area\n``A_target = A_total \u00b7 (1/\u03c1_c) / \u03a3(1/\u03c1)`` (the conservation\nlaw of \u00a71 in ``mesh-adaptation-formulation.md``). When the\nmover's eigen-clamp ``[h0/R, h0\u00b7R]`` is in play, clip the\ntarget so it represents what the mover can *actually*\nachieve, not the unbounded ideal. Then\n\n.. math::\n\n \\delta_c = \\tfrac12\\,\\log\\!\\Big(\n \\frac{A_{\\mathrm{actual},c}}{A_{\\mathrm{target},c}}\\Big)\n\n(signed, log-space symmetric: a 2\u00d7 refine needed = +0.35;\na 2\u00d7 coarsen needed = -0.35). Scale-invariant under\n``\u03c1 \u2192 \u03b1\u03c1``.\n\nReturns ``{\"rms\": ..., \"max\": ..., \"median_abs\": ...}``\nsummarising ``|\u03b4|`` over cells. A mesh already at the\nmover's achievable equidistribution gives ~0; the\npre-adapted mesh against a strongly-peaked metric gives\nO(1) or larger.\n\nCheap: one ``metric`` evaluate at cell centroids + a few\nNumPy reductions. Used by\n:func:`smooth_mesh_interior(skip_threshold=...)` to skip\nadapting when the mesh is already aligned with the target.\n\nParameters\n----------\nmesh : underworld3.discretisation.Mesh\n Triangle mesh (only 2-D for now).\nmetric : sympy / UW expression\n The target *density* \u03c1 (larger \u21d2 finer cells) \u2014 same\n object you would pass to ``smooth_mesh_interior``.\nresolution_ratio : float, optional\n The mover's eigen-clamp ``R``. When given, the\n equidistribution target areas are clipped to\n ``[A_mean / R\u00b2, A_mean \u00b7 R\u00b2]`` \u2014 the achievable band the\n mover honours \u2014 so a perfectly-adapted mesh measures\n ``\u03b4 \u2248 0``. Without it, mismatch is measured against the\n unbounded equidistribution target (so even a\n perfectly-adapted mesh has ``\u03b4 \u2260 0`` against the\n unreachable ideal).\n\nReturns\n-------\ndict\n ``{\"rms\": float, \"max\": float, \"median_abs\": float}``.", + "harvested_comments": [ + "Clip target areas to the mover's achievable band", + "[A_mean/R\u00b2, A_mean\u00b7R\u00b2] (h in [h0/R, h0\u00b7R] \u21d2", + "A in [h0\u00b2/R\u00b2, h0\u00b2\u00b7R\u00b2] = [A_mean/R\u00b2, A_mean\u00b7R\u00b2]).", + "Alignment \u2014 Pearson r of log(1/A_c) with log(\u03c1_c).", + "Equidistribution gives log(1/A) \u221d (1/d)\u00b7log(\u03c1) \u21d2 r \u2192 1." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "smooth_surface_field", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 2499, + "signature": "(field, n_iters: int = 10, alpha: float = 0.5, taubin: bool = True, mu: Optional[float] = None, passband: float = 0.1, pinned_labels: Optional[Sequence[str]] = None)", + "parameters": [ + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_iters", + "type_hint": "int", + "default": "10", + "description": "" + }, + { + "name": "alpha", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "taubin", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "mu", + "type_hint": "Optional[float]", + "default": "None", + "description": "" + }, + { + "name": "passband", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "pinned_labels", + "type_hint": "Optional[Sequence[str]]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Low-pass a scalar field over a (surface) mesh's vertex-edge graph.\n\nThe *field* analogue of the coordinate graph-Laplacian Jacobi path in\n:func:`smooth_mesh_interior`. Each sweep blends every vertex value toward\nthe mean of its edge-neighbours,\n\n.. math::\n\n h_i \\leftarrow h_i + f\\,\\Big( \\tfrac{1}{|N(i)|}\\sum_{j\\in N(i)} h_j\n - h_i \\Big),\n\nattenuating high-wavenumber (facet-scale / sawtooth) content while leaving\nthe smooth, long-wavelength field. Plain Laplacian smoothing\n(``taubin=False``) also damps the smooth part and shrinks amplitudes; the\n**Taubin** :math:`\\lambda\\,|\\,\\mu` scheme \u2014 a positive blend ``alpha``\nfollowed by a negative back-step ``mu`` each iteration \u2014 is a near-flat\npassband low-pass that leaves the mean and the long-wavelength amplitude\nessentially unchanged (the discrete analogue of a volume-preserving\nfilter).\n\nDesigned for the free-surface height field carried on a codim-1 surface\nsubmesh (:meth:`Mesh.extract_surface`): the graph operator needs only the\nedge connectivity, so it works on a 1-manifold loop where an FE solve is\nnot yet available. No global gather, no FFT \u2014 unlike a spectral\n(Fourier ``h(\u03b8)``) low-pass it is purely local on the surface graph.\n\nParameters\n----------\nfield : degree-1 scalar ``MeshVariable``\n Smoothed **in place** (its nodal values are overwritten).\nn_iters : int, default 10\n Number of Taubin (or plain-Laplacian) iterations.\nalpha : float, default 0.5\n Positive blend factor :math:`\\lambda \\in (0, 1]`.\ntaubin : bool, default True\n Apply the volume-preserving :math:`\\lambda\\,|\\,\\mu` pair each\n iteration. ``False`` \u21d2 plain Laplacian smoothing (shrinks).\nmu : float, optional\n Negative back-step factor. Default derived from ``passband`` as\n :math:`\\mu = 1/(k_{pb} - 1/\\lambda)` (the standard Taubin choice;\n gives :math:`|\\mu| \\gtrsim \\lambda`). Ignored when ``taubin`` is False.\npassband : float, default 0.1\n Taubin passband wavenumber :math:`k_{pb}` used to derive ``mu``.\npinned_labels : sequence of str, optional\n Boundary labels whose vertices are held fixed (e.g. the endpoints of\n an open surface arc). A closed loop (annulus / shell ``Upper``) has\n none, so the default ``None`` is correct there.\n\nNotes\n-----\nParallel-correct and **bit-identical serial vs parallel**. The\nneighbour-average is a single ``A.mult`` against the parallel\nvertex-vertex adjacency from :func:`_build_adjacency_matrix` (assembled\nwith GLOBAL vertex indices, so an owned row sees every neighbour \u2014 even\nthose owned by another rank not in this rank's overlap); the field is\nloaded into / read out of the global Vec via\n:func:`_build_local_to_owned_map`, and the smoothed result is scattered\nback to the field's local array (ghosts filled) with ``globalToLocal``.\nThe constant mode (eigenvalue 0) is preserved exactly on any rank count.", + "harvested_comments": [ + "Parallel vertex-vertex adjacency (entries 1.0; global indices) + the", + "1-dof-per-vertex scalar DM that owns the Vec/section layout.", + "row sums = vertex degrees", + "Standard Taubin: 1/\u03bb + 1/\u03bc = k_pb \u21d2 \u03bc = 1/(k_pb \u2212 1/\u03bb) < 0.", + "Load the field (local: owned+ghost) into the global Vec (owned rows)." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "smooth_mesh_interior", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 2626, + "signature": "(mesh, pinned_labels: Optional[Sequence[str]] = None, n_iters: int = 5, alpha: float = 0.5, metric = None, method: str = 'spring', boundary_slip: bool = False, method_kwargs: Optional[dict] = None, verbose: bool = False, skip_threshold = _UNSET, strategy: Optional[str] = None, slip_surfaces = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pinned_labels", + "type_hint": "Optional[Sequence[str]]", + "default": "None", + "description": "" + }, + { + "name": "n_iters", + "type_hint": "int", + "default": "5", + "description": "" + }, + { + "name": "alpha", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "metric", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "method", + "type_hint": "str", + "default": "'spring'", + "description": "" + }, + { + "name": "boundary_slip", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "method_kwargs", + "type_hint": "Optional[dict]", + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "skip_threshold", + "type_hint": null, + "default": "_UNSET", + "description": "" + }, + { + "name": "strategy", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "slip_surfaces", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Smooth a mesh's interior vertices, optionally toward a\nspatially-varying target spacing.\n\n**Default (``metric=None``)** \u2014 graph-Laplacian Jacobi: each\ninterior vertex is blended toward the plain mean of its edge\nneighbours,\n\n.. math::\n\n x_i^{n+1} = (1 - \\alpha)\\, x_i^n\n + \\alpha \\cdot \\frac{1}{|N(i)|}\n \\sum_{j \\in N(i)} x_j^n ,\n\nover ``n_iters`` sweeps. Equalises connectivity \u2192 equant cells.\n\n**With a ``metric``** \u2014 an elastic-spring network relaxed to\nequilibrium. Every edge is a linear spring with rest length\n``\u221d \u03c1_tgt^{-1/d}`` (``\u03c1_tgt = metric``), scaled so the mean rest\nlength equals the current mean edge length (overall scale\npreserved \u2014 pure redistribution). Damped Jacobi force iteration\nrelaxes interior nodes to force balance, with a coherent global\nsigned-area backtrack guaranteeing no cell inverts. The rest\nlength is an *absolute* target, so the mesh genuinely grades\ntoward spacing ``\u221d \u03c1_tgt^{-1/d}`` (a regime the weighted\nLaplacian / Jacobi cannot reach). ``n_iters`` and ``alpha`` are\nignored on this path (it has its own internal sweep budget). A\nLagrangian density (``f(r0.sym)`` peaked at the original outer\nradius) keeps the rest lengths fixed per material point, so the\n*design* boundary-layer grading is restored even after\nfree-surface deformation.\n\nVertices in any of ``pinned_labels`` are held fixed (preserves\nboundary geometry). The mesh's coordinate vector is updated in\nplace via ``mesh._deform_mesh`` once at the end.\n\nParameters\n----------\nmesh : underworld3.discretisation.Mesh\n The mesh to smooth. Modified in place.\npinned_labels : sequence of str, optional\n Names of boundary labels whose vertices stay fixed. If\n ``None`` (default), all non-sentinel labels on\n ``mesh.boundaries`` are pinned \u2014 i.e. every named boundary\n stays put. Pass an explicit list to release some boundaries.\nn_iters : int, default 5\n Number of Jacobi sweeps. 5-10 is typical for surface-\n deformation cleanup. **Ignored when ``metric`` is given**\n (the spring path has its own internal sweep budget).\nalpha : float, default 0.5\n Under-relaxation in ``(0, 1]`` for the Jacobi path. 1.0 is\n pure Jacobi; smaller is more damped. **Ignored when\n ``metric`` is given.**\nmetric : sympy / UW expression, optional\n Target *density* :math:`\\rho_{\\mathrm{tgt}}` (larger \u21d2\n finer cells). Typically ``f(r0.sym)`` for a refinement\n function ``f`` of a Lagrangian state variable ``r0`` (a\n degree-1 scalar MeshVariable set once to the original\n coordinate and never reassigned, so its value rides each\n material point through deformation). Should be strictly\n positive and finite. ``None`` (default) \u21d2 the\n graph-Laplacian Jacobi path, unchanged behaviour\n bit-for-bit.\nmethod : {\"spring\", \"ma\"}, default \"spring\"\n Metric-grading solver (ignored when ``metric is None``):\n\n * ``\"spring\"`` \u2014 *volumetric* elastic-spring equilibrium:\n equal edge springs (shape regulariser, equant cells, no\n slivers) + a per-cell area constraint\n ``A0 \u221d 1/\u03c1_tgt`` (the size grading), minimised by\n preconditioned nonlinear CG. **Fast** (~0.3 s on a\n res-16 Annulus), robust, scales with the metric\n amplitude; slightly anisotropic at sharp interior\n features.\n * ``\"ma\"`` \u2014 Benamou\u2013Froese\u2013Oberman convex-branch\n **Monge\u2013Amp\u00e8re** equidistribution. Highest-fidelity\n *isotropic* refinement and robust to the boundary\n treatment, but ~60\u00d7 costlier than the spring.\n * ``\"anisotropic\"`` \u2014 **tensor** metric mover: an\n M-weighted Laplace (Winslow) smooth of the coordinate\n map with an eigen-clamped, gradient-derived *anisotropic*\n metric tensor. Reshapes cells (short across a feature,\n long along it) and removes the slivers / wasted isotropic\n resolution the scalar paths leave near a boundary-peaked\n feature. Linear (one solve/component/step \u2014 cheaper than\n ``\"ma\"``). It improves cell **alignment / quality**, not\n the grading magnitude (see the cap note below); for a\n *separable* feature the explicit 1-D OT is exact and\n cheaper \u2014 ``\"anisotropic\"`` earns its keep on the general\n non-separable case.\n * ``\"mmpde\"`` \u2014 variational moving-mesh (Huang\u2013Kamenski\n MMPDE) with a full tensor (or scalar) metric; the\n recommended production mover for adaptive meshing.\n **Currently 2D-only** (triangle meshes) \u2014 a 3D mesh\n raises ``NotImplementedError``.\n\n With a fixed node count neither can exceed \u22481.3\u20131.8\u00d7\n deep/near grading (the optimal-transport \u224810\u00d7 needs *more\n nodes* \u2014 a topology change, not this smoother). See\n ``docs/developer/subsystems/mesh-metric-redistribution.md``.\nboundary_slip : bool, default False\n Let boundary nodes slide tangentially along their boundary\n (snapped back to the boundary each step \u2014 they cannot leave\n it; serial circular/spherical boundaries only). Strongly\n helps the spring (+~10 % grading, faster); near-no-op for\n ``ma`` (its natural Neumann BC already handles the\n boundary). Off by default \u2014 for a free surface the boundary\n is the moving surface, so sliding interacts with the\n free-surface coupling; enable per use-context.\nmethod_kwargs : dict, optional\n Extra tuning forwarded to the chosen metric solver (ignored\n when ``metric is None``). Keeps the shared signature clean\n while exposing the per-method knobs. For\n ``method=\"anisotropic\"`` there is **one primary knob**:\n\n * ``resolution_ratio`` (``R``, default **1.0 = exact\n no-op**) \u2014 *the* tuneable. Cells may refine to ``h0/R``\n and coarsen to ``h0\u00b7R``; the refine \u21c4 coarsen split is\n **not a parameter** \u2014 the isotropic density is\n equidistribution-normalised (``s = base\u00b7\u03c1/G``, ``G`` the\n geometric mean of \u03c1), so flat regions release exactly the\n budget the fronts consume, *complementary by the\n conservation law itself*. The eigen-clamp\n ``[h0/R, h0\u00b7R]`` is just a safety rail. ``R=1`` \u21d2\n bit-identical to the refine-only historical default (an\n exact no-op vs. every prior result). ``R\u22482`` is the\n validated production point (clean mesh through a full\n convection lifecycle, ``minA/meanA``\u22480.2, genuine\n plume-reaching de-resolution, settled physics intact).\n One number; complementary coarsening is automatic.\n * ``geom_mean_smoothing`` (``a``, default 0.25) \u2014\n *internal* temporal damping of the equidistribution\n normaliser ``G`` (not a grading knob; only acts when\n ``R>1``). ``G`` is recomputed from the instantaneous\n field every adaptation event; in a violent transient\n that lurches the whole ``\u03c1/G`` distribution across the\n fixed clamp band \u2192 clamp-saturation \u2192 the mesh visibly\n \"wobbles\". An EMA in log space\n (``lnG \u2190 a\u00b7lnG_now+(1\u2212a)\u00b7lnG_prev``) keeps the band\n centred: ``a=1`` \u21d2 no damping (instantaneous, the\n original wobbly behaviour); ``a\u22480.25`` \u21d2 strong damping\n of the startup over-reaction + steady-state contrast\n pulse. It smooths **only the one global intensity\n scalar** \u2014 the spatial \u03c1(x) pattern still tracks the\n current field every event, so the API stays single-knob\n (``R``); ``a`` carries one internal scalar across events.\n * ``relax`` (0.2) / ``n_outer`` (12) \u2014 damped-MMPDE\n under-relaxation + composed steps (early-exit\n ``outer_tol``). ``linear_solver`` (``\"direct\"`` | MUMPS |\n ``\"gamg\"``, bit-parity, parallel-scalable). ``beta``\n (200) \u2014 anisotropic-bump saturation. ``move_anisotropy``\n \u2014 optional radial/tangential move reweight.\n * **Expert overrides (not the documented API; only honoured\n when ``resolution_ratio\u22641``):** ``aniso_cap`` (2.0) and\n ``coarsen_cap`` (1.0) are the legacy two-knob clamp\n (``h_min=h0/\u221aaniso_cap``, ``h_max=h0\u00b7\u221acoarsen_cap``,\n ad-hoc ``s=base\u00b7cc^(q-1)``). Retained **bit-for-bit** so\n historical scripts reproduce; superseded by\n ``resolution_ratio``.\n\n Example::\n\n smooth_mesh_interior(\n mesh, metric=rho, method=\"anisotropic\",\n method_kwargs=dict(resolution_ratio=2.0,\n relax=0.05, n_outer=25))\nverbose : bool, default False\n Print per-sweep (Jacobi) or periodic (spring/MA) progress.\nskip_threshold : float, optional\n If set, evaluate the *misalignment* between current mesh\n cell density and the metric (via\n :func:`mesh_metric_mismatch`) and **skip the adapt** when\n misalignment is below this threshold. Misalignment is\n ``\u221a(1 \u2212 r\u00b2)`` where ``r`` is the Pearson correlation of\n ``log(1/A_cell)`` with ``log(\u03c1_cell)`` \u2014 a magnitude-free\n measure of whether cell density is aligned with the\n metric. 0 \u21d2 perfectly aligned; 1 \u21d2 orthogonal /\n anti-aligned. Ignored when ``metric is None``. Calibration\n from one of the R=1.5 stagnant-lid tests: a uniform mesh\n gives misalignment \u2248 1.00 (r \u2248 0); a freshly-adapted mesh\n gives misalignment \u2248 0.85 (r \u2248 0.52). So ``0.9`` is a\n sensible \"skip if reasonably aligned\" default for an\n adaptive convection loop; ``0.5`` is strict (only skip\n when very well aligned); ``0`` \u21d2 always adapt\n (equivalent to ``None``). Cost: one ``metric`` evaluate\n at cell centroids + a few NumPy reductions.\n\nNotes\n-----\n**Parallel implementation (Jacobi path)**: the vertex-vertex\nadjacency is assembled as a parallel PETSc AIJ matrix; each rank\ninserts entries for every locally-visible edge using GLOBAL\nvertex indices and ``mat.assemble()`` routes cross-rank\ncontributions so that owned-vertex rows are complete after\nassembly. The per-sweep update is a per-component ``A.mult``\nfollowed by a pointwise divide by the precomputed degree vector.\nResults are bit-identical (to a single ULP) between serial and\nparallel runs at any rank count.\n\n**Spring path**: serial-exact. Edge forces are accumulated over\nlocally-visible edges only, so rank-partition-boundary nodes\nunder-count their incident forces in parallel (a future PR can\nassemble the edge forces cross-rank like the Jacobi adjacency\nMat). The edge list and per-node degree are cached against the\ntopology key and rebuilt only on a topology change.\n\n**Topology preservation**: vertex IDs, DOF mappings, and the\nrank partition are unchanged. Only coordinates move. Anything\ncached against the topology version stays valid; anything\ncached against coords is invalidated by the final\n``mesh._deform_mesh`` call.\n\nExamples\n--------\nPin all named boundaries (the usual case)::\n\n import underworld3 as uw\n from underworld3.meshing import smooth_mesh_interior\n\n mesh = uw.meshing.Annulus(...)\n # ... some deformation that leaves bad cells ...\n smooth_mesh_interior(mesh, n_iters=5, alpha=0.5)\n\nPin only the outer boundary, allowing the inner to drift::\n\n smooth_mesh_interior(mesh, pinned_labels=[\"Upper\"])\n\nPin nothing (free-floating; rare \u2014 boundary will collapse)::\n\n smooth_mesh_interior(mesh, pinned_labels=[])\n\nRestore a design grading via a Lagrangian refinement metric::\n\n r0 = uw.discretisation.MeshVariable(\n \"r0\", mesh, uw.VarType.SCALAR, degree=1)\n X0 = np.asarray(mesh.X.coords)\n r0.data[:, 0] = np.sqrt((X0 ** 2).sum(axis=1)) # set once\n # ... deformation that crushes near-surface cells ...\n f = 1 + 8 * sympy.exp(-((r0.sym[0] - 1.0) / 0.12) ** 2)\n smooth_mesh_interior(mesh, metric=f)", + "harvested_comments": [ + "... some deformation that leaves bad cells ...", + "... deformation that crushes near-surface cells ...", + "slip_surfaces is the public alias for the deprecated boundary_slip;", + "resolve to a single spec threaded to the bare mover.", + "Pre-create the projected-normal field (mesh.Gamma_P1) ONCE here," + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "metric_density_from_gradient", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 3776, + "signature": "(mesh, field)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Build a target-**density** metric ``\u03c1 \u221d normalised |\u2207field|``\nfor the metric movers \u2014 the relative, fixed-node-budget\nanalogue of :func:`underworld3.adaptivity.metric_from_gradient`\n(which maps ``|\u2207field|`` to an *absolute* target edge length\nfor the MMG re-mesher; the mover has a fixed node budget so it\nredistributes *relatively* instead).\n\n.. math::\n\n \\rho = (1 + \\mathrm{amp}\\cdot t)^{\\mathrm{power}},\\qquad\n t = \\mathrm{clip}\\!\\Big(\n \\frac{|\\nabla\\mathrm{field}| - g_{lo}}\n {g_{hi} - g_{lo}}, 0, 1\\Big),\n\nwith ``g_lo, g_hi`` the lo/hi percentiles of ``|\u2207field|`` (the\nsame percentile-window idea as the adaptation metric).\n\n**What the power knob does (strategic choice).** The mover\nequidistributes ``\u03c1`` (cell area \u00d7 \u03c1 \u2248 const). Combined with\n``A_c = h_c^d`` in ``d`` dimensions that gives\n``h_c \u221d \u03c1_c^{-1/d}``. For the linear ramp ``\u03c1 \u221d |\u2207T|`` (i.e.\n``power=1``, the historical default) this means\n``h_c \u221d |\u2207T|^{-1/d}`` and the per-cell temperature change\n``\u0394T_c \u2248 |\u2207T|\u00b7h_c \u221d |\u2207T|^{1-1/d}`` \u2014 strong-gradient cells\nstill carry MORE temperature change than weak-gradient cells.\nChoosing ``power = d`` (so ``\u03c1 \u221d |\u2207T|^d``) gives\n``h_c \u221d 1/|\u2207T|`` and ``\u0394T_c \u2248 const`` \u2014 a **gradient-uniform\ntarget**: every cell carries the same temperature change.\n``power = 1`` (default) targets \"refinement of fronts /\nboundaries\" (mild grading concentrated where gradients are\nstrongest); ``power = d`` targets \"uniform per-cell error in\na piecewise-linear T interpolant\" (the natural goal for\nadvection-diffusion accuracy). Values in between blend the\ntwo; ``power < 1`` softens grading further.\n``|\u2207field|`` is L2-projected (a *first* derivative \u2014 UW3-clean)\nand the normalised ``t`` is stored in a **frozen Lagrangian\nscalar field**, so the returned metric rides material points \u2014\nrequired by the movers, which build the metric once on the\nundeformed mesh. Pass the result straight to\n:func:`smooth_mesh_interior`::\n\n rho = metric_density_from_gradient(mesh, T, amp=8.0)\n smooth_mesh_interior(mesh, metric=rho,\n method=\"anisotropic\")\n\nThe projector/fields are cached per ``(mesh, degree, name,\ntopology)``, so calling this **every step** in an adaptive loop\nis cheap and does not leak MeshVariables. Each call re-projects\nand re-freezes ``t`` at the *current* field state.\n\nParameters\n----------\nmesh : underworld3 mesh\nfield : scalar MeshVariable or sympy scalar expression\n The field whose gradient drives refinement (e.g. ``T``).\nrefinement : float, optional\n Target COARSEST:FINEST edge-length ratio ``R = h_max/h_min``\n the metric grades to. When ``> 1`` this **overrides** ``amp``\n (and the strategy default). Because the mover equidistributes\n ``\u03c1`` (so cell edge ``h \u221d \u03c1^{-1/d}``), the full ``t\u2208[0,1]``\n range gives ``h_max/h_min = (1 + amp)^{power/d}``; this is\n inverted to set ``amp = R^{d/power} - 1``. So ``R`` is a\n predictable, **mesh-independent** resolution knob (``R=2`` \u21d2\n finest cells ~2\u00d7 smaller than the coarsest). The *realised*\n ratio tracks ``R`` until the fixed node budget saturates it\n (large ``R`` is capped \u2014 going further needs h-refinement to\n add nodes, not just redistribution). ``None`` (default) \u21d2 use\n ``amp`` / the ``strategy`` preset. Exposed in the convection\n harness as ``--resolution-ratio``.\namp : float, default 8.0\n Bunching intensity: ``\u03c1_max = (1 + amp)^power`` where\n ``|\u2207field|`` is strongest. Larger \u21d2 stronger\n redistribution.\npower : float, default 1.0\n Exponent applied to the metric. ``1`` (default) =\n front-following (``\u03c1 \u221d |\u2207T|``, mild grading).\n ``d`` (mesh dimension) = gradient-uniform\n (``\u03c1 \u221d |\u2207T|^d``, uniform per-cell \u0394T). Values in\n between blend; ``<1`` softens. The strategic choice is\n between \"refine the fronts\" and \"uniform per-cell\n error\", not a free dial \u2014 see the docstring math.\nmode : {\"percentile\", \"raw\"}, default \"percentile\"\n How the gradient drives the metric. ``\"percentile\"``\n (default): \u03c1 = (1 + amp\u00b7t)^power with t the\n percentile-clipped normalised |\u2207field| \u2014 concentrates\n budget into the steepest fronts, ignores values below\n ``lo_percentile``. ``\"raw\"``: \u03c1 = |\u2207field|^power\n directly (no offset, no clipping, no amp). The mover's\n equidistribution geometric-mean normalisation handles\n the absolute scale; ``amp`` and ``lo/hi_percentile``\n are ignored. Use ``\"raw\"`` to target gradient-uniform\n per-cell \u0394T cleanly; ``\"percentile\"`` to refine only\n the top X% of gradient values.\nlo_percentile, hi_percentile : float, default 50 / 97\n ``|\u2207field|`` normalisation window (cf. the 5th/95th of\n ``adaptivity.metric_from_gradient``). Raise ``lo`` to push\n refinement only into the steepest fronts.\ndegree : int, default 1\n Polynomial degree of the projected-gradient / density\n fields (1 matches the anisotropic mover's default\n ``aux_degree``).\nname : str, optional\n Cache disambiguator. Pass distinct names if you build\n several independent gradient metrics on the *same* mesh\n simultaneously (otherwise they share the cache slot).\nsmoothing_length : float or Pint Quantity, optional\n Length-scale ``L`` for **field-side** screened-Poisson\n smoothing applied to ``field`` BEFORE the gradient is\n taken. Useful to suppress sub-grid noise in the source.\n WARNING: at ``L \u2273`` BL width this *erases* the\n boundary-layer gradient \u2014 T's transition is spread over\n ~L and the gradient peak ``T_active/h`` collapses to\n ``T_active/L``. Prefer\n ``gradient_smoothing_length`` when targeting features\n with BL-like sub-h structure.\ngradient_smoothing_length : float or Pint Quantity, optional\n Length-scale ``L`` for **gradient-side** screened-Poisson\n smoothing applied to the projected ``|\u2207field|`` field\n (via the L2-projection's ``smoothing_length``). Peak\n *location* of ``|\u2207T|`` is preserved (a BL still\n concentrates near where T transitions); only the\n spatial distribution / mesh-noise in the projection is\n smoothed. This is the principled way to break the\n metric/mesh feedback on adapted meshes without\n destroying BL features. Set ``L \u2248 h0`` (background\n mean cell size) for mild de-noising;\n ``L \u2248 2\u00b7h0`` for stronger.\n\nReturns\n-------\nsympy expression\n ``(1 + amp * t.sym[0])**power`` \u2014 Lagrangian, frozen at\n call time.", + "harvested_comments": [ + "Resolve strategy defaults \u2014 individual kwargs override.", + "`refinement` R = target COARSEST:FINEST edge-length ratio (h_max/h_min).", + "The mover equidistributes \u03c1=(1+amp\u00b7t)^power, so cell edge h \u221d \u03c1^(-1/d) and", + "the full t\u2208[0,1] range gives h_max/h_min = (1+amp)^(power/d). Invert that to", + "set amp, so R is a predictable, mesh-independent edge-length ratio. This" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "follow_metric", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 4183, + "signature": "(mesh, field) -> bool", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Move the mesh's interior nodes so cell sizes follow a target\nderived from ``|\u2207field|``.\n\nTwo-knob, cell-size-envelope API for the anisotropic node mover.\nThe user specifies how *fine* the densest cells can get and\n(optionally) how *coarse* the sparsest can get; the function\nderives the metric density and invokes the mover.\n\nCell-size envelope (approximate)\n--------------------------------\n\nThe mover's eigenvalue \u2192 cell-size map is\n:math:`h = h_0/\\sqrt{\\hat\\rho}` (after the mover's\ngeometric-mean normalisation :math:`\\hat\\rho = \\rho/G`), so\nasking for the envelope\n\n.. math::\n\n h \\;\\in\\; \\bigl[\\, h_0/\\text{refinement},\\;\n h_0\\cdot\\text{coarsening} \\,\\bigr]\n\ncorresponds to :math:`\\hat\\rho \\in [1/\\text{coarsening}^2,\n\\text{refinement}^2]` \u2014 note this is **dimension-\nindependent** (the eigenvalue \u03bb has units of 1/length\u00b2).\n\nValidation on a sharp-tanh annulus test problem shows:\n\n* **Refinement side:** achieved :math:`h_\\min` within ~5-10%\n of :math:`h_0/\\text{refinement}` for refinement \u2208 [1.5, 3].\n* **Coarsening side:** achieved :math:`h_\\max` typically\n ~2\u00d7 the requested :math:`h_0\\cdot\\text{coarsening}`. The\n mover's anisotropic cells and iterative deformation map\n together don't honour the eigenvalue clamp on a per-cell\n basis as tightly as the refinement side. This is a known\n feature of the underlying mover, not of the new API.\n\nThe :func:`mesh_metric_mismatch` diagnostic is the right tool\nfor measuring how close the achieved mesh is to the requested\nmetric in practice.\n\nMetric ansatz\n-------------\n\nEach cell's percentile rank :math:`p \\in [0,1]` in the global\n:math:`|\\nabla\\text{field}|` distribution maps to the\nlog-density via a piecewise-linear function with the break\n:math:`\\rho = 1` at\n\n.. math::\n\n p^{\\ast} \\;=\\; \\frac{\\log\\text{refinement}}\n {\\log(\\text{refinement}\\cdot\n \\text{coarsening})} .\n\nThis break point makes :math:`\\mathrm{geomean}(\\rho) = 1`\nby construction, so the mover's :math:`G`-normalisation\nleaves :math:`\\rho` unshifted and the eigenvalue clamps land\non the desired envelope. Concretely:\n\n* \"front-following\" (default) \u2014 log-:math:`\\rho` is linear\n in percentile rank on each side of :math:`p^{\\ast}`. Every\n 1% of cells contributes the same log(h) increment. Mild\n grading; the budget is spread continuously across the\n gradient distribution.\n* \"gradient-uniform\" \u2014 :math:`\\rho \\propto |\\nabla\\text{field}|^2`,\n clipped to the envelope. Targets uniform per-cell\n :math:`\\Delta\\text{field}` (the natural goal for advection-\n diffusion accuracy). The clipping makes the achieved\n grading regress to the front-following profile when the\n gradient distribution is concentrated.\n\nAuto coarsening (the budget-conserving default)\n-----------------------------------------------\n\nWith a fixed node count (no remeshing), refining one cell to\n:math:`h_0/\\text{refinement}` requires growing others by at\nleast\n\n.. math::\n\n \\text{coarsening} \\;=\\; \\text{refinement}^{\\,1/d}\n\nto absorb the freed cell area. ``coarsening=\"auto\"`` (default)\npicks exactly this minimum \u2014 anything less would mean the\nmover can't actually deliver the requested refinement.\nPass an explicit ``coarsening>auto`` to free up more budget\nfor a smoother transition zone.\n\nAdapt-on-demand\n---------------\n\nBefore invoking the mover, the current mesh is checked against\nthe requested target via\n:func:`mesh_metric_mismatch`. If the alignment is already good\n(misalignment below ``skip_threshold``), the mesh isn't\nre-adapted \u2014 the function returns ``False`` and the caller can\nkeep stepping. This lets a per-step adapt cadence become\n\"adapt only when needed\".\n\nParameters\n----------\nmesh : underworld3 mesh\n Modified in place if adaptation runs.\nfield : MeshVariable or sympy scalar expression\n The field whose gradient drives refinement.\nrefinement : float, must be >= 1.0\n Maximum local refinement, expressed as a multiplicative\n factor on the background cell size:\n :math:`h_\\min = h_0 / \\text{refinement}`. ``refinement=1``\n is a no-op (uniform metric \u21d2 background spacing).\ncoarsening : float or \"auto\", default \"auto\"\n Maximum local coarsening,\n :math:`h_\\max = h_0 \\cdot \\text{coarsening}`. ``\"auto\"``\n uses the budget-conserving minimum\n :math:`\\text{refinement}^{1/d}`. Larger values free more\n budget for smoother grading at the cost of a wider\n cell-size spread.\nmetric : {\"front-following\", \"gradient-uniform\"}, default \"front-following\"\n Strategic equidistribution rule. ``\"front-following\"``\n concentrates cells where the gradient is steepest (mild\n grading). ``\"gradient-uniform\"`` aims for the same\n per-cell field change everywhere (best for advection-\n diffusion accuracy).\nskip_threshold : float, default 0.9\n Alignment threshold for the adapt-on-demand skip. If the\n existing mesh's :func:`mesh_metric_mismatch` alignment is\n \u2265 this threshold, no adaptation happens and the function\n returns ``False``.\ngradient_smoothing_length : float or Pint Quantity, optional\n Length scale for screened-Poisson smoothing of the\n projected ``|\u2207field|`` before building the metric.\n Suppresses sub-cell metric-mesh feedback noise without\n destroying boundary-layer features. A useful default is\n ``\u2248 2 * h_0`` (background cell size).\npolish_max_iters : int, default 5\n Maximum Jacobi (graph-Laplacian) polish iterations\n applied AFTER the anisotropic mover. The polish runs\n adaptively: each iteration averages every interior\n vertex toward the mean of its edge neighbours\n (cell-quality cleanup), and the loop stops as soon as\n the worst cell-shape quality exceeds\n ``polish_quality_target``. ``polish_max_iters=0``\n disables the polish entirely.\npolish_quality_target : float, default 0.3\n Adaptive-polish stopping criterion: target minimum\n cell shape quality\n :math:`q = 4\\sqrt{3}\\,A/(e_0^2+e_1^2+e_2^2)`. ``q=1``\n is equilateral; ``q<0.3`` is the threshold below which\n cells look like visible slivers. Lower values allow\n more sliver-y cells through; higher values demand\n more polish iterations.\npolish_alpha : float, default 0.2\n Under-relaxation in ``(0, 1]`` for each Jacobi\n sweep. Lower = gentler.\nname : str, optional\n Cache disambiguator. Pass distinct names if you build\n several independent metrics on the same mesh.\nverbose : bool, default False\n Verbose mover diagnostics.\n\nReturns\n-------\nbool\n ``True`` if the mesh was moved; ``False`` if the\n skip-on-mismatch check short-circuited adaptation.\n\nExamples\n--------\nDefault usage on a stagnant-lid convection T field, with\ncoarsening picked automatically::\n\n moved = uw.meshing.follow_metric(\n mesh, T,\n refinement=3.0, # h_min = h0/3\n ) # coarsening = \u221a3 \u2248 1.73 (2D auto)\n\nWider grading transition with explicit coarsening, gradient-\nside smoothing, and the gradient-uniform rule for advection\naccuracy::\n\n uw.meshing.follow_metric(\n mesh, T,\n refinement=2.0, coarsening=2.0,\n metric=\"gradient-uniform\",\n gradient_smoothing_length=2.0 * mesh._radii.mean(),\n )\n\nSee Also\n--------\nmetric_density_from_gradient : The underlying metric builder\n (expert tool \u2014 exposes percentile / amp / power dials).\nsmooth_mesh_interior : The underlying mover (expert tool \u2014\n unaware of refinement/coarsening, takes a pre-built\n metric expression).\nmesh_metric_mismatch : The alignment / misalignment metric\n used by the skip threshold.", + "harvested_comments": [ + "h_min = h0/3", + "coarsening = \u221a3 \u2248 1.73 (2D auto)", + "Resolve auto coarsening", + "Mover's `resolution_ratio` is a SYMMETRIC eigenvalue clamp", + "(h \u2208 [h0/R, h0\u00b7R]) \u2014 too loose for either side on its own." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SphericalShell", + "kind": "function", + "file": "src/underworld3/meshing/spherical.py", + "line": 30, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a 3D spherical shell mesh.\n\nGenerates a tetrahedral mesh on a full spherical shell (or solid\nsphere if ``radiusInner=0``) using Gmsh's OpenCASCADE geometry\nkernel. Provides :math:`(r, \\theta, \\phi)` coordinates for\nconvenient representation of radial problems.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the spherical shell.\nradiusInner : float, default=0.547\n Inner radius of the spherical shell. Set to 0 for a solid\n sphere extending to the centre.\ncellSize : float, default=0.1\n Target mesh element size.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply. Each level\n approximately octruples element count.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n - ``Centre``: Centre point (if radiusInner=0)\n\n The mesh includes a refinement callback that snaps boundary\n nodes back to true spherical geometry after refinement.\n\nSee Also\n--------\nAnnulus : 2D equivalent.\nSphericalShellInternalBoundary : Spherical shell with internal surface.\nCubedSphere : Structured spherical shell mesh.\n\nExamples\n--------\nCreate a spherical shell for mantle convection:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.SphericalShell(\n... radiusOuter=1.0,\n... radiusInner=0.547,\n... cellSize=0.1\n... )\n\nCreate a solid sphere:\n\n>>> mesh = uw.meshing.SphericalShell(\n... radiusOuter=1.0,\n... radiusInner=0.0,\n... cellSize=0.1\n... )\n\nNotes\n-----\nThe inner radius default of 0.547 corresponds approximately to the\nEarth's core-mantle boundary radius ratio (3480/6371).\n\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`\n\nFor free-slip boundary conditions on vector problems (e.g., Stokes),\nuse a penalty on the normal velocity component::\n\n Gamma_N = mesh.Gamma # discrete normal, or\n Gamma_N = mesh.CoordinateSystem.unit_e_0 # analytic radial\n stokes.add_natural_bc(\n penalty * Gamma_N.dot(v_soln.sym) * Gamma_N, \"Upper\"\n )", + "harvested_comments": [ + "discrete normal, or", + "analytic radial", + "Ensure boundaries conform (if refined)", + "This is equivalent to a partial function because it already", + "knows the configuration of THIS spherical mesh and" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SphericalManifold", + "kind": "function", + "file": "src/underworld3/meshing/spherical.py", + "line": 291, + "signature": "(radius: float = 1.0, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radius", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a 2-manifold mesh of a spherical surface, embedded in 3-D.\n\nProduces a triangular surface mesh with topological dimension\n``dim=2`` and coordinate dimension ``cdim=3``: a 2-sphere lying in\nEuclidean 3-space. This is the manifold sibling of\n:func:`SphericalShell` (which builds a 3-D volume between two\nradii); both share the spherical coordinate system so\n:math:`(r, \\theta, \\phi)` accessors are available on either.\n\nThe mesh is generated by building a 3-D ball in gmsh, extracting\nits boundary surface, and meshing only the surface \u2014 no volume\ncells are produced. The resulting DM has a clean three-stratum\nchart (cells, edges, vertices) and supports PDEs solved on the\nmanifold (Laplace\u2013Beltrami, surface advection-diffusion, surface\nflow).\n\nParameters\n----------\nradius : float, default=1.0\n Radius of the sphere.\ncellSize : float, default=0.1\n Target mesh element size (chord length).\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the gmsh ``.msh`` file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2-manifold mesh with ``dim=2``, ``cdim=3``. The closed\n sphere has no boundary curve; ``boundaries`` carries a single\n ``Surface`` label covering the whole manifold for\n convenience. The mesh registers spherical coordinate\n accessors (``mesh.X.spherical.r``, ``.theta``, ``.phi``;\n symbolic ``mesh.r`` etc.) the same way :func:`SphericalShell`\n does.\n\nSee Also\n--------\nSphericalShell : 3-D volume sibling.\nextract_surface : extract a manifold from an existing volume mesh's\n boundary (alternative construction path).\n\nNotes\n-----\nThe closed sphere has 3 rigid-body rotation modes (about each\nCartesian axis) which are recorded on ``mesh._nullspace_rotations``\njust as for ``SphericalShell``. These tangent-to-surface vector\nfields lie in the kernel of the surface Stokes / shallow-water\noperators and must be projected out in solvers.\n\nExamples\n--------\nConstruct a unit-sphere manifold mesh and inspect its dimensions::\n\n >>> import underworld3 as uw\n >>> sphere = uw.meshing.SphericalManifold(radius=1.0, cellSize=0.1)\n >>> sphere.dim, sphere.cdim\n (2, 3)\n\nAccess spherical coordinates::\n\n >>> r = sphere.X.spherical.r # constant == radius\n >>> theta = sphere.X.spherical.theta # co-latitude\n >>> phi = sphere.X.spherical.phi # longitude", + "harvested_comments": [ + "constant == radius", + "co-latitude", + "the manifold itself (closed sphere has no boundary curve)", + "Extract the 2-D boundary surfaces of the ball, then discard", + "the volume \u2014 we want a surface-only mesh." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SphericalShellInternalBoundary", + "kind": "function", + "file": "src/underworld3/meshing/spherical.py", + "line": 500, + "signature": "(radiusOuter: float = 1.0, radiusInternal: float = 0.8, radiusInner: float = 0.547, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInternal", + "type_hint": "float", + "default": "0.8", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Generates a spherical shell with an internal boundary using Gmsh. The function creates a 3D mesh of a spherical shell\ndefined by outer, internal, and inner radii. Mesh size, polynomial degree, and Gmsh verbosity can be customized.\n\nParameters\n----------\nradiusOuter : float, optional\n The outer radius of the spherical shell. Default is 1.0.\nradiusInternal : float, optional\n The radius of the internal boundary. Default is 0.8.\nradiusInner : float, optional\n The inner radius of the spherical shell. Default is 0.547.\ncellSize : float, optional\n The target size for the mesh elements. Default is 0.1.\ndegree : int, optional\n The polynomial degree of the finite elements. Default is 1.\nqdegree : int, optional\n The quadrature degree for integration. Default is 2.\nfilename : str, optional\n The name of the file where the mesh will be saved. Default is None.\nrefinement : optional\n Refinement level for the mesh. Default is None.\ngmsh_verbosity : int, optional\n Gmsh output verbosity (0=quiet). Default is 0.\nverbose : bool, optional\n If True, print additional information. Default is False.\n\nReturns\n-------\nMesh\n The generated spherical shell mesh with internal boundary.\n\nExamples\n--------\n>>> mesh = uw.meshing.SphericalShellInternalBoundary(\n... radiusOuter=2.0,\n... radiusInternal=1.5,\n... radiusInner=1.0,\n... cellSize=0.05,\n... )", + "harvested_comments": [ + "This generator builds a SINGLE shell volume [radiusInner, radiusOuter] with", + "the radiusInternal sphere *embedded* as a conformal internal surface (the", + "`Internal` boundary). Because it is one OCC volume, gmsh cannot emit Inner/", + "Outer region physical groups \u2014 so the Inner/Outer cell regions used by", + "mesh.extract_region() are created AFTER import by classifying each cell by" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "SegmentofSphere", + "kind": "function", + "file": "src/underworld3/meshing/spherical.py", + "line": 787, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, longitudeExtent: float = 90.0, latitudeExtent: float = 90.0, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False, centroid: Tuple = (0.0, 0.0, 0.0))", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "longitudeExtent", + "type_hint": "float", + "default": "90.0", + "description": "" + }, + { + "name": "latitudeExtent", + "type_hint": "float", + "default": "90.0", + "description": "" + }, + { + "name": "cellSize", + "type_hint": "float", + "default": "0.1", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "centroid", + "type_hint": "Tuple", + "default": "(0.0, 0.0, 0.0)", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Generates a segment of a sphere using Gmsh. This function creates a 3D mesh of a spherical segment defined by outer and inner radii,\nand the extent in longitude and latitude. The mesh can be customized in terms of size, polynomial degree, and verbosity.\n\nParameters\n----------\nradiusOuter : float, optional\n The outer radius of the spherical segment. Default is 1.0.\nradiusInner : float, optional\n The inner radius of the spherical segment. Default is 0.547.\nlongitudeExtent : float, optional\n The angular extent in longitude (degrees). Default is 90.0.\nlatitudeExtent : float, optional\n The angular extent in latitude (degrees). Default is 90.0.\ncellSize : float, optional\n The target size for the mesh elements. Default is 0.1.\ndegree : int, optional\n The polynomial degree of the finite elements. Default is 1.\nqdegree : int, optional\n The quadrature degree for integration. Default is 2.\nfilename : str, optional\n The name of the file where the mesh will be saved. Default is None.\nrefinement : optional\n Refinement level for the mesh. Default is None.\ngmsh_verbosity : int, optional\n Gmsh output verbosity (0=quiet). Default is 0.\nverbose : bool, optional\n If True, print additional information. Default is False.\ncentroid : tuple of float, optional\n The coordinates of the centroid of the sphere segment.\n Default is (0.0, 0.0, 0.0).\n\nReturns\n-------\nMesh\n The generated spherical segment mesh.\n\nExamples\n--------\n>>> mesh = uw.meshing.SegmentofSphere(\n... radiusOuter=2.0,\n... radiusInner=1.0,\n... longitudeExtent=120.0,\n... latitudeExtent=60.0,\n... cellSize=0.05,\n... )", + "harvested_comments": [ + "Create segment of sphere", + "Add Physical groups", + "Add the volume entity to a physical group with a high tag number (99999) and name it \"Elements\"", + "Ensure boundaries conform (if refined)", + "This is equivalent to a partial function because it already" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "CubedSphere", + "kind": "function", + "file": "src/underworld3/meshing/spherical.py", + "line": 1048, + "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, numElements: int = 5, degree: int = 1, qdegree: int = 2, simplex: bool = False, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "parameters": [ + { + "name": "radiusOuter", + "type_hint": "float", + "default": "1.0", + "description": "" + }, + { + "name": "radiusInner", + "type_hint": "float", + "default": "0.547", + "description": "" + }, + { + "name": "numElements", + "type_hint": "int", + "default": "5", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "qdegree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "simplex", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "gmsh_verbosity", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a cubed-sphere mesh with structured elements.\n\nGenerates a spherical shell mesh using the cubed-sphere projection,\nwhich maps six cube faces onto the sphere. Produces hexahedral\nelements (or tetrahedra if ``simplex=True``) with uniform resolution.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the spherical shell.\nradiusInner : float, default=0.547\n Inner radius of the spherical shell.\nnumElements : int, default=5\n Number of elements along each edge of each cube face. Total\n elements approximately ``6 * numElements^2`` per radial layer.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nsimplex : bool, default=False\n If False, use hexahedral elements. If True, subdivide into\n tetrahedra (simplex elements).\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n\nSee Also\n--------\nSphericalShell : Unstructured tetrahedral spherical mesh.\nSegmentofSphere : Partial spherical shell.\n\nExamples\n--------\nCreate a cubed-sphere mesh with 10 elements per edge:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.CubedSphere(\n... radiusOuter=1.0,\n... radiusInner=0.55,\n... numElements=10\n... )\n\nNotes\n-----\nThe cubed-sphere projection avoids polar singularities present in\nlatitude-longitude grids, providing quasi-uniform element sizes\nacross the sphere. This is particularly advantageous for global\ngeodynamics simulations.\n\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "harvested_comments": [ + "Make copies", + "if not simplex:", + "Generate Mesh", + "# Note these numbers should not be hard-wired", + "print(f\"Refinement callback - spherical\", flush=True)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 438, + "signature": "(self) -> Optional[str]", + "parameters": [], + "returns": "Optional[str]", + "existing_docstring": "Units for this variable (None if dimensionless).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 443, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Check if this variable has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 448, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Direct access to vertex data with optional unit awareness.\n\nReturns:\n UnitAwareArray if units are set, otherwise plain numpy array.\n Shape is (n_vertices,) for size=1, or (n_vertices, size) otherwise.\n\nNote:\n When units are set, modifications via array operations automatically\n sync to pyvista storage and mark proxy as stale. For raw numpy arrays,\n call mark_stale() after modifications.", + "harvested_comments": [ + "Import here to avoid circular imports", + "Create callback to sync changes to pyvista and mark stale" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "data", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 481, + "signature": "(self, values) -> None", + "parameters": [ + { + "name": "values", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Set vertex data and mark proxy as stale.\n\nIf values have units (magnitude attribute), the magnitude is extracted\nfor storage in pyvista. Units are tracked separately.", + "harvested_comments": [ + "Strip units if present" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "mark_stale", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 494, + "signature": "(self) -> None", + "parameters": [], + "returns": "None", + "existing_docstring": "Mark the proxy as stale so it will be recomputed on next .sym access.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "mask", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 499, + "signature": "(self) -> sympy.Expr", + "parameters": [], + "returns": "sympy.Expr", + "existing_docstring": "Distance-based mask: 1 near surface, 0 far away.\n\nUses the surface's signed distance field with the configured profile.\nMust be explicitly applied in expressions: use `var.sym * var.mask`.\n\nReturns:\n sympy.Expr representing the mask (1 near, 0 far)\n\nRaises:\n ValueError: If mask_width was not set in add_variable()\n\nExample:\n >>> friction = surface.add_variable(\"friction\", mask_width=0.1)\n >>> friction.data[:] = 0.6\n >>> eta = friction.sym[0] * friction.mask # Masked value", + "harvested_comments": [ + "Masked value" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "sym", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 530, + "signature": "(self) -> sympy.Matrix", + "parameters": [], + "returns": "sympy.Matrix", + "existing_docstring": "Symbolic representation for use in expressions.\n\nReturns:\n sympy.Matrix that can be used in Underworld expressions.\n\nNote:\n On first access (or after mark_stale()), this triggers interpolation\n from surface vertices to mesh nodes. The interpolation uses inverse\n distance weighting from nearby surface vertices.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceVariable", + "is_public": true + }, + { + "name": "dim", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 720, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Spatial dimension (2 or 3).\n\nDetected from mesh.dim if available, otherwise from control points shape.", + "harvested_comments": [ + "Try to get from mesh", + "Infer from control points", + "Default to 3D" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "is_2d", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 745, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "True if this is a 2D surface (1D curve in 2D space).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "symbol", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 750, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Short LaTeX-friendly symbol for math expressions.\n\nUsed in distance field expressions like $d_F$ instead of\nthe full variable name {surf_fault_distance}.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "symbol", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 759, + "signature": "(self, value: str) -> None", + "parameters": [ + { + "name": "value", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Set the math symbol. Marks distance field as stale if changed.", + "harvested_comments": [ + "If distance var exists, it needs to be recreated with new symbol" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "control_points", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 796, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(N, 3) array of control points defining the surface.\n\nReturns coordinates in physical (dimensional) units when the\nunits system is active, matching the ``mesh.X.coords`` convention.\nInternally, points are stored in model (non-dimensional) space.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "set_control_points", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 807, + "signature": "(self, points: np.ndarray) -> None", + "parameters": [ + { + "name": "points", + "type_hint": "np.ndarray", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Set control points and mark discretization as stale.\n\nCoordinates are stored internally in model (non-dimensional) space,\nmatching the mesh's internal coordinate representation (``mesh._coords``).\n\nArgs:\n points: (N, 2) or (N, 3) array of points in one of these forms:\n\n - **Raw numpy array**: Assumed to be in model coordinates\n (same space as ``mesh._coords``). When units are active,\n this means nondimensional coordinates.\n - **Pint Quantity / UnitAwareArray**: Automatically converted\n to model coordinates via the scaling system.\n\n For 2D points, a z=0 column is appended automatically.", + "harvested_comments": [ + "Handle unit conversion: Pint Quantity or UnitAwareArray \u2192 model coords", + "Use the mesh's coordinate system scale factor for consistency with", + "the output gateway (_dimensionalise_coords).", + "Convert to base SI (meters) then divide by scale factor", + "No scaling active \u2014 just strip units" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "vertices", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 861, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(N, 3) array of vertex positions.\n\nReturns coordinates in physical (dimensional) units when the\nunits system is active, matching the ``mesh.X.coords`` convention.\nInternally, vertices are stored in model (non-dimensional) space.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "n_vertices", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 873, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Number of vertices in the discretized surface.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "n_triangles", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 880, + "signature": "(self) -> int", + "parameters": [], + "returns": "int", + "existing_docstring": "Number of triangles in the surface.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "normals", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 887, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(N, 3) array of vertex normals (point normals from pyvista).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "triangles", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 895, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(M, 3) array of triangle vertex indices.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "face_centers", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 905, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(M, 3) array of triangle centroids.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "face_normals", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 913, + "signature": "(self) -> Optional[np.ndarray]", + "parameters": [], + "returns": "Optional[np.ndarray]", + "existing_docstring": "(M, 3) array of face normals (cell normals from pyvista).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "pv_mesh", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 921, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "PyVista PolyData mesh in the same coordinate space as ``mesh.X.coords``.\n\nReturns a shallow copy whose points have been dimensionalised so that\nthey overlay correctly with ``vis.mesh_to_pv_mesh(mesh)``. The\ninternal ``_pv_mesh`` stays in nondimensional model space so that\ndistance calculations remain consistent with the solver.\n\nReturns None if the surface has not been discretized.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "is_discretized", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 939, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Whether the surface has been discretized.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "discretize", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 950, + "signature": "(self, offset: float = 0.01, n_segments: int = None) -> None", + "parameters": [ + { + "name": "offset", + "type_hint": "float", + "default": "0.01", + "description": "" + }, + { + "name": "n_segments", + "type_hint": "int", + "default": "None", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Discretize control points into a surface mesh.\n\nFor 3D surfaces: Uses pyvista delaunay_2d to create a triangulated mesh.\nFor 2D surfaces: Uses scipy to fit a spline and create a polyline.\n\nArgs:\n offset: (3D only) Height offset for delaunay_2d (controls curvature tolerance).\n n_segments: (2D only) Number of line segments, or None for auto.\n\nRaises:\n ImportError: If pyvista not available\n ValueError: If points too sparse for discretization\n RuntimeError: If discretization fails", + "harvested_comments": [ + "Clear stale flags" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "deform_vertices", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1071, + "signature": "(self, displacement: np.ndarray) -> None", + "parameters": [ + { + "name": "displacement", + "type_hint": "np.ndarray", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Deform surface vertices in-place (no re-discretization).\n\nThis modifies vertex positions while keeping topology fixed.\nNormals are automatically recomputed.\n\nArgs:\n displacement: (n_vertices, 3) array of displacements to add\n\nNote:\n This does NOT update control points. If you want topology changes,\n use set_control_points() instead and call discretize().", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "distance", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1104, + "signature": "(self) -> 'uw.discretisation.MeshVariable'", + "parameters": [], + "returns": "'uw.discretisation.MeshVariable'", + "existing_docstring": "Signed distance from mesh nodes to surface (lazily computed).\n\nThe signed distance is positive on one side of the surface and\nnegative on the other. Use sympy.Abs(surface.distance.sym[0]) for\nunsigned distance, or use influence_function() which does this\nautomatically.\n\nReturns:\n MeshVariable with signed distance values at each mesh node.\n Access .sym[0] for use in expressions.\n\nRaises:\n RuntimeError: If mesh not set or surface not discretized\n\nExample:\n >>> # Use signed distance for different properties on each side\n >>> d = surface.distance.sym[0]\n >>> prop = sympy.Piecewise((upper_value, d > 0), (lower_value, True))\n >>>\n >>> # Use absolute distance for symmetric influence\n >>> mask = sympy.Piecewise((1, sympy.Abs(d) < width), (0, True))", + "harvested_comments": [ + "Use signed distance for different properties on each side", + "Use absolute distance for symmetric influence" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "abs_distance", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1142, + "signature": "(self) -> 'uw.discretisation.MeshVariable'", + "parameters": [], + "returns": "'uw.discretisation.MeshVariable'", + "existing_docstring": "Unsigned (edge-clamped) distance from mesh nodes to the surface.\n\nUnlike ``Abs(self.distance.sym[0])``, this field is safe to interpolate\nnear a surface edge. The signed ``distance`` field changes sign across\nthe surface, and its zero-contour extends along the surface's\ninfinite-line/plane PAST the finite edge; interpolating it (any\nnon-nodal evaluation) and taking ``Abs`` produces a spurious near-zero\nvalley beyond the edge. This unsigned field is ``>= 0`` everywhere and,\nbeyond the edge, is simply the radial distance to the endpoint, so its\ninterpolant never crosses zero there. Used by :meth:`influence_function`.\n\nReturns:\n MeshVariable with unsigned distance values at each mesh node.\n Access ``.sym[0]`` for use in expressions.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "influence_function", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1254, + "signature": "(self, width: float, value_near: Union[float, sympy.Expr] = 1.0, value_far: Union[float, sympy.Expr] = 0.0, profile: str = 'step') -> sympy.Expr", + "parameters": [ + { + "name": "width", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "value_near", + "type_hint": "Union[float, sympy.Expr]", + "default": "1.0", + "description": "" + }, + { + "name": "value_far", + "type_hint": "Union[float, sympy.Expr]", + "default": "0.0", + "description": "" + }, + { + "name": "profile", + "type_hint": "str", + "default": "'step'", + "description": "" + } + ], + "returns": "sympy.Expr", + "existing_docstring": "Create level-set-like influence function based on distance.\n\nCreates a sympy expression that varies from value_near (at the surface)\nto value_far (far from the surface) based on the chosen profile.\n\nUses the unsigned, edge-clamped distance field (:attr:`abs_distance`),\nso the influence is symmetric on both sides of the surface AND decays\ncorrectly beyond a finite edge (it does not bleed along the surface's\nline/plane past the end). For asymmetric behaviour, access the signed\ndistance directly via ``surface.distance.sym[0]``.\n\nParameters\n----------\nwidth : float\n Characteristic width of the transition zone.\nvalue_near : float or sympy.Expr, optional\n Value at/near the surface. Default is 1.0.\nvalue_far : float or sympy.Expr, optional\n Value far from the surface. Default is 0.0.\nprofile : str, optional\n Transition profile type. One of ``\"step\"`` (sharp transition),\n ``\"linear\"`` (linear ramp), ``\"gaussian\"`` (smooth decay),\n or ``\"smoothstep\"`` (C1-continuous Hermite). Default is ``\"step\"``.\n\nReturns\n-------\nsympy.Expr\n Expression that can be used in Underworld equations.\n\nExamples\n--------\nStep function for fault zone viscosity:\n\n>>> eta = surface.influence_function(\n ... width=0.05,\n ... value_near=0.01,\n ... value_far=1.0,\n ... profile=\"step\",\n ... )\n >>>\n >>> # Gaussian decay for smooth transitions\n >>> eta = surface.influence_function(\n ... width=0.1,\n ... value_near=friction.sym, # Variable on surface\n ... value_far=1.0,\n ... profile=\"gaussian\",\n ... )", + "harvested_comments": [ + "Gaussian decay for smooth transitions", + "Variable on surface", + "Accept quantities and convert to nondimensional mesh coordinates", + "Use the UNSIGNED (edge-clamped) distance FIELD, not Abs() of the signed", + "field. The signed field's zero-contour extends past a finite edge, so" + ], + "status": "complete", + "needs": [], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "add_variable", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1342, + "signature": "(self, name: str, size: int = 1, proxy_degree: int = 1, units: Optional[str] = None, mask_width: Optional[float] = None, mask_profile: str = 'gaussian') -> SurfaceVariable", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "size", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "proxy_degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "units", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "mask_width", + "type_hint": "Optional[float]", + "default": "None", + "description": "" + }, + { + "name": "mask_profile", + "type_hint": "str", + "default": "'gaussian'", + "description": "" + } + ], + "returns": "SurfaceVariable", + "existing_docstring": "Add a variable on surface vertices.\n\nCreates a SurfaceVariable stored in pyvista point_data with\nsymbolic access via .sym for use in expressions.\n\nArgs:\n name: Variable name\n size: Number of components (1 for scalar, 3 for vector)\n proxy_degree: Degree of proxy MeshVariable for .sym access\n units: Optional units for this variable (e.g., \"Pa\", \"m/s\")\n mask_width: Width for distance-based mask (enables .mask property)\n mask_profile: Profile for mask function (\"step\", \"linear\", \"gaussian\", \"smoothstep\")\n\nReturns:\n SurfaceVariable that can be modified via .data and used via .sym\n\nExample:\n >>> # Variable with units and mask\n >>> friction = surface.add_variable(\"friction\", size=1, mask_width=0.1)\n >>> friction.data[:] = 0.6\n >>>\n >>> # Use in expressions with explicit masking\n >>> eta = friction.sym[0] * friction.mask", + "harvested_comments": [ + "Variable with units and mask", + "Use in expressions with explicit masking" + ], + "status": "complete", + "needs": [], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "get_variable", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1389, + "signature": "(self, name: str) -> SurfaceVariable", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "SurfaceVariable", + "existing_docstring": "Get an existing variable by name.\n\nArgs:\n name: Variable name\n\nReturns:\n SurfaceVariable\n\nRaises:\n KeyError: If variable doesn't exist", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "variables", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1404, + "signature": "(self) -> Dict[str, SurfaceVariable]", + "parameters": [], + "returns": "Dict[str, SurfaceVariable]", + "existing_docstring": "Dictionary of all variables on this surface.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "save", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1410, + "signature": "(self, filename: str) -> None", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Save surface with all variables to VTK file.\n\nAll SurfaceVariable data is automatically included in the VTK file\nas point_data arrays.\n\nArgs:\n filename: Output path (.vtk or .vtp)", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "refinement_metric", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1451, + "signature": "(self, h_near, h_far, width = None, profile: str = 'linear', name: str = None) -> 'MeshVariable'", + "parameters": [ + { + "name": "h_near", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "h_far", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "width", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "profile", + "type_hint": "str", + "default": "'linear'", + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Create a metric field for mesh adaptation based on distance from this surface.\n\nReturns a MeshVariable containing refinement metric values that can\nbe passed directly to mesh.adapt(). Higher metric values produce finer\nmesh (smaller elements).\n\nParameters\n----------\nh_near : float or quantity\n Target edge length near the surface (smaller = finer mesh).\n Accepts a plain float (in nondimensional mesh coordinates) or a\n ``uw.quantity`` (e.g., ``uw.quantity(3, \"km\")``) which is\n automatically nondimensionalised.\nh_far : float or quantity\n Target edge length far from the surface (larger = coarser mesh).\n Same unit handling as *h_near*.\nwidth : float or quantity, optional\n Distance over which to transition from h_near to h_far.\n If None, defaults to 2 * h_far. Same unit handling as *h_near*.\nprofile : str, optional\n Transition profile: \"linear\", \"smoothstep\", or \"gaussian\".\n Default is \"linear\".\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"{surface_name}_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing refinement metric values.\n\nNotes\n-----\n**Metric Tensor Mathematics**\n\nFor isotropic mesh adaptation, MMG/PETSc uses a metric tensor:\n\n.. math::\n\n M = h^{-2} \\cdot I\n\nwhere :math:`h` is the target edge length and :math:`I` is the identity\nmatrix. This relationship is **dimension-independent** - the same formula\napplies in 2D and 3D because the metric defines edge lengths, not areas\nor volumes.\n\nThe adaptation algorithm seeks to make all edges have unit length in the\nmetric space (i.e., :math:`\\mathbf{e}^T M \\mathbf{e} = 1` for edge vector\n:math:`\\mathbf{e}`). Higher metric values produce smaller elements.\n\n**Refinement Ratio**\n\nThe refinement ratio is ``h_far / h_near``. For example, if ``h_near=0.01``\nand ``h_far=0.1``, the mesh will be ~10\u00d7 finer near the surface.\n\n**Element Count Control**\n\nTo maintain approximately the same total element count while refining\nnear the surface, the far-field should use similar h to the original\nmesh's cell size. The refined region is small, so coarsening the far-field\nslightly can compensate.\n\nReferences\n----------\n.. [1] MMG Platform documentation: http://www.mmgtools.org/\n.. [2] Alauzet, F. \"Metric-based anisotropic mesh adaptation\" (2010)\n\nExamples\n--------\n>>> fault = uw.meshing.Surface(\"fault\", mesh, fault_points)\n>>> fault.discretize()\n>>>\n>>> # With plain floats (nondimensional coordinates)\n>>> metric = fault.refinement_metric(h_near=0.005, h_far=0.05)\n>>> mesh.adapt(metric)\n>>>\n>>> # With quantities (automatic nondimensionalisation)\n>>> metric = fault.refinement_metric(\n... h_near=uw.quantity(3, \"km\"),\n... h_far=uw.quantity(30, \"km\"),\n... width=uw.quantity(10, \"km\"),\n... )\n>>> mesh.adapt(metric)", + "harvested_comments": [ + "With plain floats (nondimensional coordinates)", + "With quantities (automatic nondimensionalisation)", + "Accept quantities and convert to nondimensional mesh coordinates", + "Create metric MeshVariable", + "Get distance values directly from the distance MeshVariable" + ], + "status": "complete", + "needs": [], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "from_trace", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1580, + "signature": "(cls, name: str, mesh: 'Mesh', trace_points: np.ndarray, depth_range: tuple, n_depth_layers: int = None, depth_spacing = None, trace_resolution = None, smoothing: float = 0.0, dip: float = None, dip_direction: str = 'right', symbol: str = None) -> 'Surface'", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "trace_points", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "depth_range", + "type_hint": "tuple", + "default": null, + "description": "" + }, + { + "name": "n_depth_layers", + "type_hint": "int", + "default": "None", + "description": "" + }, + { + "name": "depth_spacing", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "trace_resolution", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "smoothing", + "type_hint": "float", + "default": "0.0", + "description": "" + }, + { + "name": "dip", + "type_hint": "float", + "default": "None", + "description": "" + }, + { + "name": "dip_direction", + "type_hint": "str", + "default": "'right'", + "description": "" + }, + { + "name": "symbol", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "'Surface'", + "existing_docstring": "Create a surface by extruding a polyline trace to depth.\n\nThis is the recommended way to create fault surfaces from map-view\ntrace data. The trace polyline is optionally interpolated to a\ntarget resolution, then extruded to depth to create a ruled\nsurface with explicit triangulation.\n\nWhen *dip* is specified, the surface follows a parabolic curve\nfrom vertical at the surface to the given dip angle at maximum\ndepth. The offset is applied perpendicular to the local trace\ndirection. This produces geologically realistic fault geometry\nwhere faults are steep near the surface and flatten at depth.\n\nFor geographic meshes, trace points are (lon, lat) in degrees and\ndepths are physical distances below the ellipsoid surface. The\nellipsoid parameters are read from the mesh automatically.\n\nFor Cartesian meshes, trace points are (x, y) in model coordinates\nand depth is the z-coordinate (downward positive).\n\nParameters\n----------\nname : str\n Surface identifier.\nmesh : Mesh\n Computational mesh. For geographic meshes the ellipsoid is\n read from ``mesh.CoordinateSystem.ellipsoid``.\ntrace_points : ndarray, shape (N, 2)\n Surface trace polyline. For geographic meshes: ``(lon, lat)``\n in degrees. For Cartesian meshes: ``(x, y)`` in the same\n coordinate space as control points (model or physical,\n depending on whether units are active).\ndepth_range : tuple of (min_depth, max_depth)\n Depth extent for extrusion (positive downward). Each value\n can be a plain float (km for geographic, model coords for\n Cartesian) or a ``uw.quantity``.\nn_depth_layers : int, optional\n Number of depth levels (including surface and deepest).\n If None, computed from *depth_spacing*. If both are None,\n defaults to 7 layers.\ndepth_spacing : float or quantity, optional\n Spacing between depth layers. Alternative to *n_depth_layers*.\ntrace_resolution : float or quantity, optional\n Target point spacing along the trace. If None the trace is\n used as-is. For geographic meshes this is in km; for\n Cartesian meshes it is in model coordinates.\nsmoothing : float\n Spline smoothing parameter for ``splprep`` (0 = interpolate\n exactly through points, >0 allows smoothing).\ndip : float, optional\n Dip angle in degrees measured from horizontal. ``90`` means\n vertical (no offset), ``45`` means equal horizontal and\n vertical extent at maximum depth. When *None* (default),\n the surface is extruded vertically.\n\n The surface profile is parabolic: vertical at the surface\n (``depth = depth_range[0]``) and reaching *dip* at the\n deepest layer.\ndip_direction : str\n Direction of the dip offset relative to the trace:\n ``\"right\"`` (default) or ``\"left\"`` when looking along the\n trace from first to last point.\nsymbol : str, optional\n Short LaTeX symbol for expressions (e.g. ``\"F\"``).\n\nReturns\n-------\nSurface\n A fully discretized surface ready for use.\n\nExamples\n--------\n>>> # Geographic mesh \u2014 trace in lon/lat, depths in km\n>>> trace = np.column_stack([lon_points, lat_points])\n>>> s = Surface.from_trace(\n... \"fault_1\", mesh, trace,\n... depth_range=(uw.quantity(0, \"km\"), uw.quantity(30, \"km\")),\n... depth_spacing=uw.quantity(5, \"km\"),\n... trace_resolution=uw.quantity(3, \"km\"),\n... dip=60, # 60\u00b0 from horizontal, parabolic profile\n... )", + "harvested_comments": [ + "Geographic mesh \u2014 trace in lon/lat, depths in km", + "60\u00b0 from horizontal, parabolic profile", + "For geographic: depths are in km (or quantities \u2192 km)", + "For Cartesian: depths in model coordinates", + "sensible default" + ], + "status": "complete", + "needs": [], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "from_vtk", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1839, + "signature": "(cls, filename: str, mesh: 'Mesh' = None, name: str = None) -> 'Surface'", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": "None", + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "'Surface'", + "existing_docstring": "Load surface from VTK file.\n\nAll point_data arrays in the VTK file are automatically wrapped\nas SurfaceVariables.\n\nArgs:\n filename: Path to VTK file (.vtk or .vtp)\n mesh: Computational mesh (required for .sym access)\n name: Name for the surface. If None, uses filename stem.\n\nReturns:\n Surface with all variables from VTK file\n\nRaises:\n FileNotFoundError: If file doesn't exist\n ImportError: If pyvista not available", + "harvested_comments": [ + "Compute normals if not present", + "Wrap existing point_data as SurfaceVariables", + "Skip built-in normals" + ], + "status": "complete", + "needs": [], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "compute_normals", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1893, + "signature": "(self, consistent_normals: bool = True) -> None", + "parameters": [ + { + "name": "consistent_normals", + "type_hint": "bool", + "default": "True", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Recompute vertex and face normals.\n\nArgs:\n consistent_normals: If True, attempt to make normals consistently oriented", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "flip_normals", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1907, + "signature": "(self) -> None", + "parameters": [], + "returns": "None", + "existing_docstring": "Flip the direction of all normals.\n\nThis directly negates the normal vectors. Note that pyvista's\ncompute_normals() uses consistent orientation so we can't rely\non flip_faces() to reverse normals - we must negate them explicitly.", + "harvested_comments": [ + "Ensure normals are computed", + "Directly negate the point normals", + "Also negate cell normals if present" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Surface", + "is_public": true + }, + { + "name": "add", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1981, + "signature": "(self, surface: Surface) -> None", + "parameters": [ + { + "name": "surface", + "type_hint": "Surface", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Add a surface to the collection.\n\nArgs:\n surface: Surface to add\n\nRaises:\n ValueError: If surface with same name already exists", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "add_from_vtk", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1998, + "signature": "(self, filename: str, mesh: 'Mesh' = None, name: str = None) -> Surface", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": "None", + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "Surface", + "existing_docstring": "Load and add a surface from VTK file.\n\nArgs:\n filename: Path to VTK file\n mesh: Computational mesh\n name: Name for the surface. If None, uses filename stem.\n\nReturns:\n The loaded Surface", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "remove", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2018, + "signature": "(self, name: str) -> Surface", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "Surface", + "existing_docstring": "Remove and return a surface from the collection.\n\nArgs:\n name: Name of surface to remove\n\nReturns:\n The removed Surface\n\nRaises:\n KeyError: If surface not found", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "names", + "kind": "property", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2047, + "signature": "(self) -> List[str]", + "parameters": [], + "returns": "List[str]", + "existing_docstring": "List of surface names.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "compute_distance_field", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2051, + "signature": "(self, mesh: 'Mesh', distance_var: 'MeshVariable' = None, variable_name: str = 'surface_distance') -> 'MeshVariable'", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "distance_var", + "type_hint": "'MeshVariable'", + "default": "None", + "description": "" + }, + { + "name": "variable_name", + "type_hint": "str", + "default": "'surface_distance'", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Compute minimum distance from mesh points to any surface.\n\nArgs:\n mesh: The mesh to compute distances on\n distance_var: Optional existing MeshVariable to store results\n variable_name: Name for new variable if distance_var is None\n\nReturns:\n MeshVariable with distance values (scalar)", + "harvested_comments": [ + "Ensure all surfaces are discretized", + "Create or use existing variable", + "Get mesh coordinates in model (internal) space", + "to match surface point coordinates", + "Initialize with large distance" + ], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "transfer_normals", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2111, + "signature": "(self, mesh: 'Mesh', coords: np.ndarray = None, normal_var: 'MeshVariable' = None, variable_name: str = 'surface_normals') -> 'MeshVariable'", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": "np.ndarray", + "default": "None", + "description": "" + }, + { + "name": "normal_var", + "type_hint": "'MeshVariable'", + "default": "None", + "description": "" + }, + { + "name": "variable_name", + "type_hint": "str", + "default": "'surface_normals'", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Transfer surface normals to mesh points via nearest-neighbor.\n\nFor each mesh point, finds the closest surface face and copies\nthat face's normal vector.\n\nArgs:\n mesh: The mesh to transfer normals to\n coords: Optional coordinates to query. If None, uses mesh.data\n normal_var: Optional existing MeshVariable\n variable_name: Name for new variable\n\nReturns:\n MeshVariable with normal vectors (3 components)", + "harvested_comments": [ + "Ensure all surfaces are discretized", + "Get query coordinates in model (internal) space", + "to match surface point coordinates", + "Create or validate output variable", + "Build combined arrays of all face centers and normals" + ], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "influence_function", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2174, + "signature": "(self, mesh: 'Mesh', width: float, value_near: Union[float, sympy.Expr] = 1.0, value_far: Union[float, sympy.Expr] = 0.0, profile: str = 'step') -> sympy.Expr", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "width", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "value_near", + "type_hint": "Union[float, sympy.Expr]", + "default": "1.0", + "description": "" + }, + { + "name": "value_far", + "type_hint": "Union[float, sympy.Expr]", + "default": "0.0", + "description": "" + }, + { + "name": "profile", + "type_hint": "str", + "default": "'step'", + "description": "" + } + ], + "returns": "sympy.Expr", + "existing_docstring": "Create combined influence function from all surfaces.\n\nArgs:\n mesh: Computational mesh\n width: Characteristic width of the transition zone\n value_near: Value at/near surfaces\n value_far: Value far from surfaces\n profile: Transition profile (step, linear, gaussian, smoothstep)\n\nReturns:\n sympy.Expr based on combined distance field", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "refinement_metric", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2214, + "signature": "(self, mesh: 'Mesh', h_near, h_far, width = None, profile: str = 'linear', variable_name: str = 'fault_metric') -> 'MeshVariable'", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "h_near", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "h_far", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "width", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "profile", + "type_hint": "str", + "default": "'linear'", + "description": "" + }, + { + "name": "variable_name", + "type_hint": "str", + "default": "'fault_metric'", + "description": "" + } + ], + "returns": "'MeshVariable'", + "existing_docstring": "Create a combined refinement metric for mesh adaptation.\n\nComputes a single metric field using the minimum unsigned distance\nacross all surfaces in the collection. This creates **2 MeshVariables**\n(distance + metric) regardless of how many surfaces are in the\ncollection, avoiding the O(N\u00b2) DM-rebuild cost of computing per-surface\nmetrics in a loop.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nh_near : float or quantity\n Target edge length near any surface.\nh_far : float or quantity\n Target edge length far from surfaces.\nwidth : float or quantity, optional\n Transition distance. Defaults to ``2 * h_far``.\nprofile : str\n ``\"linear\"``, ``\"smoothstep\"``, or ``\"gaussian\"``.\nvariable_name : str\n Name for the metric MeshVariable.\n\nReturns\n-------\nMeshVariable\n Scalar metric field suitable for ``mesh.adapt()``.", + "harvested_comments": [ + "Compute (or reuse) the collection-wide minimum unsigned distance" + ], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "compute_nearest_fields", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2266, + "signature": "(self, mesh: 'Mesh', fault_width = None, normal_var_name: str = 'fault_n', id_var_name: str = 'fault_id', distance_var_name: str = 'd_faults', weight_var_name: str = 'fault_w') -> dict", + "parameters": [ + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "fault_width", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "normal_var_name", + "type_hint": "str", + "default": "'fault_n'", + "description": "" + }, + { + "name": "id_var_name", + "type_hint": "str", + "default": "'fault_id'", + "description": "" + }, + { + "name": "distance_var_name", + "type_hint": "str", + "default": "'d_faults'", + "description": "" + }, + { + "name": "weight_var_name", + "type_hint": "str", + "default": "'fault_w'", + "description": "" + } + ], + "returns": "dict", + "existing_docstring": "Compute per-node nearest-surface fields for rheology.\n\nFor every mesh node, finds the closest surface vertex across all\nsurfaces and returns:\n\n- **normal**: unit normal of the nearest surface vertex\n- **id**: surface identifier for the nearest surface\n- **distance**: minimum unsigned distance to any surface\n- **weight** *(optional)*: Gaussian influence ``exp(-0.5*(d/width)\u00b2)``\n\nThis consolidates what would otherwise be manual KDTree code into a\nsingle reusable call, and creates only 3\u20134 MeshVariables total.\n\nParameters\n----------\nmesh : Mesh\n The computational mesh.\nfault_width : float or quantity, optional\n Gaussian half-width for the weight field. If ``None``, the\n weight field is omitted from the returned dict.\nnormal_var_name, id_var_name, distance_var_name, weight_var_name : str\n Names for the output MeshVariables.\n\nReturns\n-------\ndict\n Keys: ``\"normal\"``, ``\"id\"``, ``\"distance\"``, and optionally\n ``\"weight\"``. Values are MeshVariable instances.", + "harvested_comments": [ + "Ensure discretized", + "ND model coords", + "triangulation normals", + "Use a numeric ID: try float(sid), fall back to index" + ], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "create_weakness_function", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2381, + "signature": "(self, distance_var: 'MeshVariable', fault_width: float, eta_weak: float = 0.01, eta_background: float = 1.0) -> sympy.Expr", + "parameters": [ + { + "name": "distance_var", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + }, + { + "name": "fault_width", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "eta_weak", + "type_hint": "float", + "default": "0.01", + "description": "" + }, + { + "name": "eta_background", + "type_hint": "float", + "default": "1.0", + "description": "" + } + ], + "returns": "sympy.Expr", + "existing_docstring": "Create Piecewise viscosity function for fault weakness.\n\nDEPRECATED: Use influence_function() instead.\n\nArgs:\n distance_var: MeshVariable containing distances\n fault_width: Width of the weak zone\n eta_weak: Viscosity within weak zone\n eta_background: Viscosity outside weak zone\n\nReturns:\n sympy.Piecewise expression", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SurfaceCollection", + "is_public": true + }, + { + "name": "fault_metric_tensor", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2484, + "signature": "(mesh, faults, refinement = 3.0, width = 'auto', base = 1.0)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "faults", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "refinement", + "type_hint": null, + "default": "3.0", + "description": "" + }, + { + "name": "width", + "type_hint": null, + "default": "'auto'", + "description": "" + }, + { + "name": "base", + "type_hint": null, + "default": "1.0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Build the analytic, Eulerian **normal-aligned anisotropic metric\ntensor** ``M(x)`` for refining a thin band of cells **across** one or more\ncodimension-1 faults, for the supplied-tensor r-adapt mover.\n\nPass the result straight to the mover::\n\n M = uw.meshing.fault_metric_tensor(mesh, faults, refinement=3.0)\n uw.meshing.smooth_mesh_interior(\n mesh, metric=M, method=\"anisotropic\", boundary_slip=False,\n method_kwargs=dict(n_outer=12, relax=0.4))\n\nConstruction \u2014 summed over every fault segment ``i`` (normal ``n_i``,\npoint-to-segment distance ``d_i(x)``):\n\n.. math::\n\n M(x) = \\mathtt{base}\\,\\Big[\\,I\n + (R^2 - 1)\\textstyle\\sum_i e^{-(d_i(x)/W)^2}\\, n_i n_i^{\\mathsf T}\\Big].\n\nAt a fault the across-fault eigenvalue is ``base\u00b7R\u00b2`` (cell size ``h_0/R``)\nand the along-fault eigenvalue is ``base`` (size ``h_0``): a thin strip\nrefined **only across** the fault, so there is no along-fault budget\ncompetition and the band centres on the line. Used directly as the\nmover's tensor ``D``; the overall ``base`` scale is irrelevant to the\nmover (only the ``R\u00b2`` anisotropy ratio and the spatial variation matter).\n\nParameters\n----------\nmesh : Mesh\n 2D mesh (the anisotropic mover is 2D-only).\nfaults : Surface | array | list\n The fault geometry, in **mesh coordinate space**: a :class:`Surface`\n (uses its control-point polyline), an ``(N>=2, 2|3)`` polyline array,\n or a list mixing those (each polyline segment contributes a bump with\n its own normal \u2014 handles 1/2/3 faults, parallel or not, straight or\n kinked).\nrefinement : float, default 3.0\n ``R`` \u2014 the across-fault refinement ratio. Cells refine to ``\u2248 h_0/R``\n across the fault (eigenvalue ratio ``R\u00b2:1``). Larger ``R`` \u21d2 finer\n across-fault cells (down to the fixed-node-budget floor).\nwidth : float | quantity | \"auto\", default \"auto\"\n ``W`` \u2014 the half-width (length-scale) of the refined strip. ``\"auto\"``\n \u2248 ``h_0/6`` (the mesh's mean cell size / 6 \u2014 resolvable yet tight).\n **Smaller ``W`` centres the band more tightly** (the residual offset\n scales with ``W``), but must stay resolvable by the (Eulerian-refined)\n mesh \u2014 too thin (``\u2272 h_0/12``) and the strip under-resolves on the\n starting mesh. ``h_0/4 \u2026 h_0/8`` is the sweet spot.\nbase : float, default 1.0\n Overall isotropic scale (mover-irrelevant; kept for generality).\n\nReturns\n-------\nsympy.Matrix\n The ``2\u00d72`` analytic metric tensor ``M(x)`` (a function of\n ``mesh.CoordinateSystem.X``), to pass as ``metric=`` with\n ``method=\"anisotropic\"``.", + "harvested_comments": [ + "TRUE global mean edge length (sum/count). Averaging per-rank means", + "(allreduce(mean)/size) mis-weights ranks with unequal edge counts and", + "lets an empty partition's sentinel 1.0 pollute the result." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "fault_comb_metric", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2596, + "signature": "(mesh, faults, cell_size, n_across = 4, amplitude = 6.0, tooth_width = None, combine = 'sum')", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "faults", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cell_size", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_across", + "type_hint": null, + "default": "4", + "description": "" + }, + { + "name": "amplitude", + "type_hint": null, + "default": "6.0", + "description": "" + }, + { + "name": "tooth_width", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "combine", + "type_hint": null, + "default": "'sum'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Build a scalar **comb** metric ``\u03c1(x)`` that refines a band of a\ncontrolled number of roughly-**uniform** cells *across* one or more faults,\nfor the isotropic equidistribution mover (``method=\"ma\"``).\n\nPass the result straight to the mover::\n\n rho = uw.meshing.fault_comb_metric(mesh, faults, cell_size=0.006,\n n_across=4)\n uw.meshing.smooth_mesh_interior(\n mesh, metric=rho, method=\"ma\",\n method_kwargs=dict(n_outer=1, n_picard=25)) # single-shot\n\nUse the **single-shot** map (``n_outer=1``): one Caffarelli-clean\nMonge\u2013Amp\u00e8re solve, untangled by construction (no folding), with no\nouter-iteration compounding and nothing to tune \u2014 the most robust\nconfiguration, and the comb's teeth give the single map all the row\nstructure it needs (~``n_across``\u22121 even layers, centred). ``n_outer=2``\nrealises a touch more of the requested ``n_across`` (the single map is\nmildly node-budget-capped) at ~1.6\u00d7 the cost; rarely needed.\n\n**Why a comb.** An equidistribution mover places node density \u221d \u221a\u03c1, so a\nsingle peaked \"refine-this-band\" metric piles all the nodes at the maximum\n(finest at the fault, coarsening out \u2014 *graded*, not uniform). The comb\ninstead places **discrete equal teeth at the exact distances where node\nlayers are wanted** \u2014 ``d = 0, dx, 2 dx, \u2026`` \u2014 so the mover drops one node\n**row at each tooth**: equal teeth \u21d2 evenly-spaced rows \u21d2 a roughly-uniform\nband of cell size ``dx``. The ``d=0`` tooth sits on the fault, so a layer\nis pinned to the line (this also *centres* the band, even for two close\nfaults). Per fault the distance is the **min over its segments**, so a\ncurved/polyline fault gets bands that follow the curve (offset curves), not\na tangle of per-segment bands. The realised band is ~2.5:1 in cell size\n(the metric valleys between teeth still want to coarsen) \u2014 uniform *enough*\nfor a slip rheology; a perfectly uniform band needs added nodes (h-adapt).\n\n.. math::\n\n \u03c1(x) = 1 + A \\sum_i \\sum_{k=0}^{m} \\exp\\!\\big(-((d_i(x) - k\\,dx)/w)^2\\big),\n\nteeth ``k = 0\u2026\u230an_across/2\u230b``, ``d_i`` the distance to fault ``i``.\n\nParameters\n----------\nmesh : Mesh\n 2D mesh. (The isotropic equidistribution movers \u2014 ``ma``/``ot`` \u2014 are\n 2D-only; 3D would need a 3D equidistribution mover, which does not yet\n exist, so this builder is 2D-only.)\nfaults : Surface | array | list\n Fault geometry in mesh coordinate space \u2014 a :class:`Surface`, an\n ``(N>=2, 2|3)`` polyline array, or a list mixing those. Each fault's\n band is built from its own min-distance (handles 1/2/3 faults, any\n orientation, straight or curved).\ncell_size : float\n ``dx`` \u2014 the tooth spacing = the target uniform across-fault cell size.\nn_across : int, default 4\n Number of elements across each band; teeth fill the half-width\n ``(n_across/2)*cell_size`` on each side of the fault.\namplitude : float, default 6.0\n ``A`` \u2014 how strongly each tooth attracts a node row (contrast vs the\n unrefined background). ~6 is a good operating point.\ntooth_width : float, optional\n Gaussian half-width of each tooth. Default ``cell_size/4`` \u2014 narrow\n enough for distinct rows, wide enough to be resolvable on the starting\n mesh (``\u2272 cell_size/6`` can be sub-cell and fail to form a row).\ncombine : {\"sum\", \"max\"}, default \"sum\"\n How to combine faults. ``\"sum\"`` (default) superposes the per-fault\n combs (fine for separated faults); ``\"max\"`` takes the strongest comb\n (cleaner when two faults are closer than a band width, avoiding\n doubled teeth in the gap).\n\nReturns\n-------\nsympy.Expr\n The scalar comb metric ``\u03c1(x)``, to pass as ``metric=`` with\n ``method=\"ma\"``.", + "harvested_comments": [ + "single-shot", + "min-distance = the fault" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "compose_metrics", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2711, + "signature": "(metrics, compose = 'max')", + "parameters": [ + { + "name": "metrics", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "compose", + "type_hint": null, + "default": "'max'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Combine several scalar density metrics into one, for the\nequidistribution mover (``method=\"ma\"``).\n\nEach item may be either a metric (a scalar sympy expression or\nMeshVariable) or a ``(metric, weight)`` tuple. The default ``\"max\"``\ncomposition is a **weighted maximum on the excess density**\n\n.. math::\n\n \\rho_{\\mathrm{combined}}(x) = 1 + \\max_i\\;\n w_i\\,\\bigl(\\rho_i(x) - 1\\bigr),\n\nso equal weights reduce to plain ``max(\u03c1_i)`` (\"refine to the finest\ndemand from any feature\") and larger ``w_i`` amplifies that feature's\nrelative demand (the way to e.g. make a fault \"heavier\" than a thermal\nboundary layer in the same run). The result is itself a valid scalar\ndensity (``\u2265 1``).\n\nExamples\n--------\n::\n\n rho_T = uw.meshing.metric_density_from_gradient(mesh, T,\n metric_choice=\"arc-length\")\n rho_F = uw.meshing.fault_comb_metric(mesh, faults, cell_size=0.008)\n rho = uw.meshing.compose_metrics([(rho_T, 1.0), (rho_F, 3.0)]) # fault heavier\n uw.meshing.smooth_mesh_interior(mesh, metric=rho, method=\"ma\",\n method_kwargs=dict(n_outer=1, n_picard=25))\n\nParameters\n----------\nmetrics : sequence\n Items are scalar metrics or ``(metric, weight)`` tuples.\ncompose : {\"max\"}, default \"max\"\n Composition operator. Only ``\"max\"`` (weighted-max-on-excess) is\n implemented; the kwarg exists to leave room for other strategies.\n\nReturns\n-------\nsympy.Expr\n The composed scalar density.", + "harvested_comments": [ + "fault heavier" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "fault_metric", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2836, + "signature": "(mesh, faults, method = 'ma', **kwargs)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "faults", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "method", + "type_hint": null, + "default": "'ma'", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Build the fault-refinement metric appropriate for the chosen\nadaptation ``method``, from one shared physical intent: *resolve\n``n_across`` elements of size ``cell_size`` across a band around the\nfault(s)*.\n\nThe three movers consume **different metric objects with different\nsemantics**, so this facade unifies the *intent* and emits the right\nrepresentation \u2014 it does not pretend they are interchangeable:\n\n=================== ============================ ===========================\n``method`` returns pass to\n=================== ============================ ===========================\n``\"ma\"`` (default) scalar comb density (sympy) ``smooth_mesh_interior(\n method=\"ma\")``\n``\"anisotropic\"`` 2\u00d72 tensor (sympy Matrix) ``smooth_mesh_interior(\n method=\"anisotropic\")``\n``\"adapt\"``/``\"mmg\"`` ``h\u207b\u00b2`` MeshVariable ``mesh.adapt(...)``\n=================== ============================ ===========================\n\n**``cell_size`` is honoured differently by each** \u2014 this is the key\ndistinction, not a detail:\n\n* ``\"adapt\"`` (MMG) **adds nodes**, so ``cell_size`` is an *absolute*,\n *exact* target: you get a genuinely uniform band of that spacing\n (topology changes).\n* ``\"ma\"`` / ``\"anisotropic\"`` only **redistribute a fixed node budget**\n (topology preserved), so ``cell_size`` is a *target*: ``\"ma\"`` reaches\n a roughly-uniform ~2.5:1 band near it; ``\"anisotropic\"`` grades (finest\n at the fault), is the most node-efficient, and ``n_across`` is only\n indicative.\n\nParameters\n----------\nmesh : Mesh (2D)\nfaults : Surface | array | list\n Fault geometry (``Surface``, polyline array, or a list); passed\n through to the per-method builder.\nmethod : {\"ma\", \"anisotropic\", \"adapt\"/\"mmg\"}, default \"ma\"\ncell_size : float (keyword-only, required)\n Target across-fault cell size. Exact for ``adapt``; a target for the\n r-adapt methods.\nn_across : int, default 4\n Elements across the band \u2192 band half-width ``(n_across/2)\u00b7cell_size``.\nh_far : float, optional\n ``adapt`` only \u2014 far-field edge length (default \u2248 mesh cell size).\nname : str\n ``adapt`` only \u2014 name for the metric MeshVariable.\n**kwargs\n Forwarded to the underlying builder (e.g. ``amplitude``,\n ``tooth_width``, ``combine`` for ``ma``; ``base`` for\n ``anisotropic``).\n\nReturns\n-------\nsympy.Expr | sympy.Matrix | MeshVariable\n The metric object for the chosen ``method`` (see table).\n\nExamples\n--------\n::\n\n # uniform-ish band, fixed topology (the slip-rheology recipe)\n rho = uw.meshing.fault_metric(mesh, faults, method=\"ma\",\n cell_size=0.006, n_across=4)\n uw.meshing.smooth_mesh_interior(mesh, metric=rho, method=\"ma\",\n method_kwargs=dict(n_outer=1, n_picard=25))", + "harvested_comments": [ + "uniform-ish band, fixed topology (the slip-rheology recipe)", + "translate absolute intent -> relative refinement ratio + strip width" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "mesh", + "kind": "property", + "file": "src/underworld3/model.py", + "line": 181, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "The primary mesh for this model", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "list_meshes", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 206, + "signature": "(self) -> Dict[int, Any]", + "parameters": [], + "returns": "Dict[int, Any]", + "existing_docstring": "List all meshes registered with this model", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_mesh", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 210, + "signature": "(self, mesh_id: int)", + "parameters": [ + { + "name": "mesh_id", + "type_hint": "int", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get a mesh by ID from the model registry", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_primary_mesh", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 214, + "signature": "(self, mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set a mesh as the primary mesh for this model.\n\nParameters:\n-----------\nmesh : uw.discretisation.Mesh\n Mesh to set as primary (must already be registered)", + "harvested_comments": [ + "Register the mesh first", + "Just update the primary mesh pointer" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_mesh", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 384, + "signature": "(self, mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set or replace the primary mesh for this model.\n\nThis method handles:\n- Registering mesh if not already registered\n- Setting as primary mesh\n- Incrementing version for change tracking\n\nParameters:\n-----------\nmesh : uw.discretisation.Mesh\n The new mesh to use as primary for this model", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_variable", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 448, + "signature": "(self, name: str, mesh = None, swarm = None)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "swarm", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get a variable by name from the model registry.\n\nParameters:\n-----------\nname : str\n Variable name to look up\nmesh : Mesh, optional\n If provided, look for variable specifically on this mesh\nswarm : Swarm, optional\n If provided, look for variable specifically on this swarm\n\nReturns:\n--------\nVariable or None", + "harvested_comments": [ + "Try exact name first", + "If mesh/swarm specified, verify it matches", + "Look for qualified name", + "Look for qualified name", + "No specific container requested, return whatever we found" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_qualified_name", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 502, + "signature": "(self, variable)", + "parameters": [ + { + "name": "variable", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get the fully qualified name for a variable.\n\nReturns the name that uniquely identifies this variable in the model registry,\nwhich may include mesh/swarm ID qualifiers to resolve namespace conflicts.\n\nParameters:\n-----------\nvariable : MeshVariable or SwarmVariable\n Variable to get qualified name for\n\nReturns:\n--------\nstr or None : Qualified name if found, None otherwise", + "harvested_comments": [ + "Search through all registered names" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "transfer_variable_data", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 524, + "signature": "(self, source_var, target_var)", + "parameters": [ + { + "name": "source_var", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "target_var", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Transfer data from source variable to target variable using global_evaluate.\n\nThis enables persistent variable identity across mesh changes by transferring\ndata from variables on old meshes to variables on new meshes.\n\nParameters:\n-----------\nsource_var : MeshVariable or SwarmVariable\n Source variable to transfer data from\ntarget_var : MeshVariable or SwarmVariable\n Target variable to transfer data to\n\nReturns:\n--------\nbool : True if transfer successful, False otherwise", + "harvested_comments": [ + "Import global_evaluate for mesh-to-mesh transfer", + "Get target coordinates based on variable type", + "Use attribute detection for wrapper compatibility", + "Mesh variable (direct or wrapped)", + "Swarm variable" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "list_variables", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 573, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "List all variables registered with this model", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "list_swarms", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 577, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "List all swarms registered with this model", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "add_solver", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 581, + "signature": "(self, name: str, solver)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Register a solver with this model", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_solver", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 586, + "signature": "(self, name: str)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get a solver by name from the model registry", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "tracker", + "kind": "property", + "file": "src/underworld3/model.py", + "line": 591, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Snapshot-managed record of where this run is.\n\nHolds ``time`` / ``step`` / ``dt`` (pre-seeded conventions)\nplus any quantity you assign \u2014 ``model.tracker.foo = ...``.\nEverything on the tracker is captured by :meth:`snapshot` and\nreverted by :meth:`restore`; loose Python variables are not.\nSolvers do not depend on it; using it is optional.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "save_state", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 613, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Save the model's current state \u2014 memory or disk, one entry point.\n\nWithout ``file``, captures an in-memory :class:`Snapshot`\ntoken suitable for in-run backtracking (\"stash for\ntimesteps\"). The token is plain Python / numpy \u2014 fast to\nproduce, fast to restore, does not survive the process.\n\nWith ``file=``, writes a persistent on-disk snapshot at\nthat path (plus a sibling ``.bulk/`` directory holding the\nbulk PETSc + swarm sidecars). Survives the process; suitable\nfor same-model restart and postprocessing workflows.\n\nEither way the captured state is the full model: all\nregistered meshes and mesh-variables, all swarms with\nper-particle data, all solver-internal state-bearers\n(:class:`ModelTracker`, ``DDt`` instances, anything else\nexposing the ``Snapshottable`` contract).\n\nParameters\n----------\nfile : str, optional\n Path to write a disk snapshot to. If omitted, an in-memory\n token is returned instead.\n\nReturns\n-------\nSnapshot\n When called without ``file`` \u2014 pass to\n :meth:`load_state` to restore.\nstr\n When ``file`` is given \u2014 the path the snapshot was\n written to (same as ``file``).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "load_state", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 654, + "signature": "(self, source) -> None", + "parameters": [ + { + "name": "source", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Restore the model from a previously saved state \u2014 memory or disk.\n\n``source`` is either:\n\n - a :class:`Snapshot` token returned by an earlier\n ``save_state()`` call (in-memory restore \u2014 bit-exact),\n - a path string to a disk-snapshot file (disk restore \u2014\n same-rank, same-model contract; mesh-rebuild on read is\n v1.2 scope).\n\nRaises\n------\nTypeError\n ``source`` is neither a :class:`Snapshot` nor a string.\n:class:`SnapshotInvalidatedError`\n The captured state no longer matches what is registered\n (mesh adapted, state-bearer missing, ...).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "define_parameter", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 686, + "signature": "(self, name: str, ptype = None, **kwargs)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "ptype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Define a new parameter with validation rules.\n\nNOTE: Parameter system not yet implemented. Use model.materials dict directly.\n\nParameters\n----------\nname : str\n Parameter path (e.g., 'material.viscosity', 'solver.tolerance')\nptype : ParameterType, optional\n Parameter type for validation (not used yet)\n**kwargs : dict\n Additional arguments", + "harvested_comments": [ + "TODO: Implement when parameter system is ready" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_material", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 706, + "signature": "(self, name: str, properties: Dict[str, Any])", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "properties", + "type_hint": "Dict[str, Any]", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set material properties (simple dictionary).\n\nParameters:\n-----------\nname : str\n Material name (e.g., 'mantle', 'crust', 'plume')\nproperties : dict\n Dictionary of property_name -> value/expression\n\nExample:\n--------\n>>> model.set_material('mantle', {'viscosity': 1e21, 'density': 3300})", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_material", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 724, + "signature": "(self, name: str)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get material properties by name", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_reference_quantities", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 728, + "signature": "(self, verbose = False, nondimensional_scaling = True, **quantities)", + "parameters": [ + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "nondimensional_scaling", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "**quantities", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set reference quantities for automatic units scaling.\n\nThis method enables the hybrid SymPy+Pint units workflow by allowing users\nto specify their problem in natural units, from which the system derives\noptimal scaling for numerical conditioning.\n\nBy default, this automatically enables non-dimensionalization for solvers,\nensuring consistent behavior between user-facing units and solver internals.\n\nParameters\n----------\nverbose : bool, optional\n If True, print diagnostic information about dimensional analysis\n and scale derivation. Default: False.\nnondimensional_scaling : bool, optional\n Enable automatic non-dimensionalization for solvers (default: True).\n When True (recommended), solver operations work in non-dimensional\n [0-1] space while user-facing values remain in physical units.\n Set to False for expert mode (dimensional units only, no scaling).\n Disabling this may cause numerical conditioning issues.\n**quantities : dict\n Named reference quantities using Pint units or UWQuantity objects,\n e.g. ``domain_depth=uw.quantity(2900, \"km\")``.\n\nRaises\n------\nRuntimeError\n If called after a mesh has been created (units are locked)\n\nNotes\n-----\nThis method creates a Pint-native registry with model-specific constants\nusing the _constants pattern for optimal numerical conditioning.\n\nThe default behavior (nondimensional_scaling=True) ensures user-facing\nvalues in physical units, solver operations in well-conditioned\nnon-dimensional [0-1] space, and automatic conversions.", + "harvested_comments": [ + "Check if units are locked", + "Store verbose flag for use in helper methods", + "Convert UWQuantity to Pint Quantity for consistency", + "IMPORTANT: Use the global uw.units registry to ensure all units are compatible", + "Check if this is a UWQuantity" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_reference_quantities", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 906, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the reference quantities for this model.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "fundamental_scales", + "kind": "property", + "file": "src/underworld3/model.py", + "line": 911, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Access the derived fundamental scales for dimensional analysis.\n\nReturns the canonical fundamental scaling quantities [L], [M], [T], [\u03b8]\nthat were automatically derived from the reference quantities via dimensional analysis.\nThese scales are used throughout the system for automatic non-dimensionalization.\n\nThe system extracts these fundamental scales from user-provided reference quantities\n(which can have any domain-specific names) through pure dimensional analysis.\nThis provides a domain-agnostic API independent of parameter naming.\n\nReturns\n-------\ndict\n Dictionary mapping canonical dimension names to their scaling quantities:\n - 'length': Length scale [L] (e.g., 1000 m)\n - 'time': Time scale [T] (e.g., 1 Myr)\n - 'mass': Mass scale [M] (e.g., 1e24 kg)\n - 'temperature': Temperature scale [\u03b8] (e.g., 1000 K)\n - Plus any additional dimensions (velocity, viscosity, pressure, etc.)\n\nExample\n-------\n>>> model = uw.get_default_model()\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(2900, \"km\"),\n... plate_velocity=uw.quantity(5, \"cm/year\"),\n... mantle_viscosity=uw.quantity(1e21, \"Pa*s\"),\n... temperature_difference=uw.quantity(3000, \"K\")\n... )\n>>> # Access derived fundamental scales (NOT the user-provided reference quantities)\n>>> L0 = model.fundamental_scales['length'] # 2900 km\n>>> T0 = model.fundamental_scales['time'] # ~1.8 Myr\n>>> eta0 = model.fundamental_scales['viscosity'] # 1e21 Pa*s\n>>> DeltaT = model.fundamental_scales['temperature'] # 3000 K\n\nNotes\n-----\n- These are the ROUNDED fundamental scales stored internally after dimensional analysis\n- The scales may have been rounded to nice values for O(1) numerical conditioning\n- Use these scales for physical interpretation of non-dimensional solutions\n- Do NOT use user-provided reference quantity names (domain-specific)", + "harvested_comments": [ + "Access derived fundamental scales (NOT the user-provided reference quantities)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "has_units", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 957, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if this model has units scaling enabled.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "validate_reference_quantities", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 961, + "signature": "(self, raise_on_error = False)", + "parameters": [ + { + "name": "raise_on_error", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Validate that all registered variables with units have required reference quantities.\n\nThis method checks each variable and reports any missing reference quantities\nneeded to properly derive scaling coefficients.\n\nParameters:\n-----------\nraise_on_error : bool, optional\n If True, raise ValueError on validation failure. If False (default),\n return validation results without raising.\n\nReturns:\n--------\ndict\n Validation results with keys:\n - 'valid': bool - Overall validation status\n - 'warnings': list of warning messages\n - 'errors': list of error messages\n\nExample:\n--------\n>>> model = uw.get_default_model()\n>>> model.set_reference_quantities(domain_depth=uw.quantity(1000, \"km\"))\n>>> mesh = uw.meshing.StructuredQuadBox(...)\n>>> v = uw.discretisation.MeshVariable('v', mesh, 2, degree=2, units='m/s')\n>>> result = model.validate_reference_quantities()\n>>> if not result['valid']:\n... print(\"\\n\".join(result['errors']))", + "harvested_comments": [ + "Check all registered variables", + "Skip variables without units", + "Validate this variable", + "Also check if scaling_coefficient is still 1.0" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_coordinate_unit", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 1021, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the coordinate unit for this model.\n\nReturns the length unit from reference quantities if set,\notherwise returns None (dimensionless).\n\nReturns:\n--------\npint.Unit or None\n Coordinate unit object (e.g., ) or None if no units set\n\nExample:\n--------\n>>> model.set_reference_quantities(domain_depth=uw.quantity(500, \"km\"))\n>>> model.get_coordinate_unit()\n # Or if using engineering rounding\n\nChanged in 2025-10-16: Now returns pint.Unit objects instead of strings\nfor consistency and to enable natural unit arithmetic (e.g., var.units / mesh.units).", + "harvested_comments": [ + "Or if using engineering rounding", + "Check if we have reference quantities with length scale", + "Look for any length-dimensioned quantity", + "Check if this is a Pint quantity (has _pint_qty attribute)", + "Get the Pint unit object directly (not the string property)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_as_default", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 1097, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Explicitly set this model as the default model.\n\nThis is useful when working with multiple models (e.g., loading from disk)\nand you want to explicitly control which model is active.\n\nReturns:\n--------\nModel\n Returns self for method chaining\n\nExample:\n--------\n>>> model1 = uw.Model()\n>>> model2 = uw.Model()\n>>> model2.set_as_default() # Make model2 the active default\n>>> assert uw.get_default_model() is model2", + "harvested_comments": [ + "Make model2 the active default" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_unit_aliases", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 1454, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Get all available unit aliases for user reference.\n\nReturns\n-------\ndict\n Dictionary mapping dimensions to their available aliases.\n\nExamples\n--------\n>>> model.get_unit_aliases()\n{\n 'length': {\n 'ascii': ['L_ref', 'L_scale', 'L_model', 'length_scale'],\n 'unicode': '\u2112',\n 'display': '\u2112',\n 'technical': '_1000km'\n },\n ...\n}", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "derive_fundamental_scalings", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 1828, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Derive fundamental scaling units (length, time, mass, temperature) from reference quantities.\n\nThis method analyzes the dimensional structure of reference quantities to automatically\ndetermine optimal fundamental scalings for the problem domain.\n\nReturns\n-------\ndict\n Dictionary of fundamental scalings with keys like '[length]', '[time]', '[mass]', '[temperature]'\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> scalings = model.derive_fundamental_scalings()\n>>> scalings['[length]'] # Derived from plate_velocity\n>>> scalings['[time]'] # Derived from plate_velocity\n>>> scalings['[temperature]'] # Direct from mantle_temperature", + "harvested_comments": [ + "Derived from plate_velocity", + "Derived from plate_velocity", + "Direct from mantle_temperature", + "Check for over-determined systems before proceeding", + "Import units (Pint) for dimensional analysis" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_fundamental_scales", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2079, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Get fundamental scales in user-friendly format.\n\nReturns the derived fundamental scaling quantities (length, time, mass, temperature)\nthat the model uses for dimensional analysis. These are the base scales from which\nall other quantities are converted to model units.\n\nIMPORTANT: Returns the ROUNDED scales stored in _fundamental_scales, not re-derived.\nThe scales may have been rounded to nice values for O(1) numerical conditioning.\n\nReturns\n-------\ndict\n Dictionary mapping dimension names to their scaling quantities:\n - 'length': Length scale quantity (e.g., 1000 m, rounded from 500 m)\n - 'time': Time scale quantity (e.g., 1 Myr, rounded)\n - 'mass': Mass scale quantity (e.g., 1e11 kg, rounded)\n - 'temperature': Temperature scale quantity (e.g., 1000 K, rounded)\n - Plus any additional dimensions detected (current, substance, etc.)\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... domain_depth=500*uw.units.m, # Will round to 1000 m\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> scales = model.get_fundamental_scales()\n>>> scales['length'] # 1000 meter (rounded, not 500)", + "harvested_comments": [ + "Will round to 1000 m", + "1000 meter (rounded, not 500)", + "Return the ROUNDED scales, not re-derived ones!", + "The _fundamental_scales dict contains rounded values for O(1) conditioning", + "_fundamental_scales already has user-friendly keys ('length', 'time', etc.)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_scale_for_dimensionality", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2126, + "signature": "(self, dimensionality: dict)", + "parameters": [ + { + "name": "dimensionality", + "type_hint": "dict", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get appropriate reference scale for given dimensionality.\n\nFirst checks if user explicitly provided a reference quantity with this exact\ndimensionality (e.g., diffusivity=1e-6 m\u00b2/s). If found, uses that directly.\nOtherwise, computes from fundamental scales (length, time, mass, temperature).\n\nThis design ensures that explicit user-provided scales take precedence over\nderived scales, which is important for physical accuracy. For example, thermal\ndiffusivity (~1e-6 m\u00b2/s) is orders of magnitude different from L\u00b2/t derived\nfrom mantle convection length and time scales.\n\nParameters\n----------\ndimensionality : dict\n Pint dimensionality dictionary, e.g., {'[length]': 1, '[time]': -1}\n for velocity, {'[mass]': 1, '[length]': -1, '[time]': -2} for pressure\n\nReturns\n-------\npint.Quantity\n Reference scale with appropriate dimensionality\n\nRaises\n------\nValueError\n If dimensionality requires scales not available in fundamental scales\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_depth=uw.quantity(2900, \"km\"), ...)\n>>> velocity_dim = {'[length]': 1, '[time]': -1}\n>>> v_scale = model.get_scale_for_dimensionality(velocity_dim)\n>>> # v_scale = 2900 km / (time_scale) in m/s\n\n>>> pressure_dim = {'[mass]': 1, '[length]': -1, '[time]': -2}\n>>> p_scale = model.get_scale_for_dimensionality(pressure_dim)\n>>> # p_scale = M/(L\u00b7T\u00b2) in Pa\n\n>>> # Explicit diffusivity takes precedence over L\u00b2/t\n>>> model.set_reference_quantities(\n... length=uw.quantity(2900, \"km\"),\n... time=uw.quantity(1e15, \"s\"),\n... diffusivity=uw.quantity(1e-6, \"m**2/s\") # Explicit!\n... )\n>>> diff_dim = {'[length]': 2, '[time]': -1}\n>>> diff_scale = model.get_scale_for_dimensionality(diff_dim)\n>>> # diff_scale = 1e-6 m\u00b2/s (explicit), NOT L\u00b2/t = 8.41e-3 m\u00b2/s", + "harvested_comments": [ + "v_scale = 2900 km / (time_scale) in m/s", + "p_scale = M/(L\u00b7T\u00b2) in Pa", + "Explicit diffusivity takes precedence over L\u00b2/t", + "diff_scale = 1e-6 m\u00b2/s (explicit), NOT L\u00b2/t = 8.41e-3 m\u00b2/s", + "PRIORITY 1: Check if user explicitly provided a reference quantity" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_model_base_units", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2242, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Get model base units in compact, parseable SI prefix form.\n\nReturns a dictionary mapping dimension names to their compact SI unit representations.\nUses Pint's to_compact() to automatically select readable SI prefixes (e.g., 'Mm' for\nmegameters, 'Ps' for petaseconds).\n\nReturns\n-------\ndict\n Dictionary with dimension names as keys and compact SI unit strings as values\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> base_units = model.get_model_base_units()\n>>> base_units['length'] # \"2.9 megameter\" (parseable)\n>>> base_units['time'] # \"1.83 petasecond\" (parseable)", + "harvested_comments": [ + "\"2.9 megameter\" (parseable)", + "\"1.83 petasecond\" (parseable)", + "Convert to compact SI prefix form (e.g., Mm, Ps, etc.)", + "Return as parseable string like \"2.9 megameter\"", + "Fallback to base units if compact fails" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_scale_summary", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2360, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Get a human-readable summary of all fundamental scales.\n\nReturns a formatted string showing the fundamental scales derived from\nreference quantities, including how each scale was derived and what\nreference quantities it makes close to unity.\n\nReturns\n-------\nstr\n Multi-line string with formatted scale summary\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> print(model.get_scale_summary())\nFundamental Scales Summary:\n\nLength Scale: 2900 kilometer\n - From: mantle_depth\n - Makes: mantle_depth \u2248 1.0 in model units\n\nTime Scale: 580 kilometer\u00b7year/centimeter\n - From: length_scale \u00f7 plate_velocity\n - Makes: plate_velocity \u2248 1.0 in model units\n\nMass Scale: 1.68e+27 kg (equivalent)\n - From: mantle_viscosity \u00d7 length_scale \u00d7 time_scale\n - Makes: mantle_viscosity \u2248 1.0 in model units\n\nTemperature Scale: 1500 kelvin\n - From: mantle_temperature\n - Makes: mantle_temperature \u2248 1.0 in model units", + "harvested_comments": [ + "Define display order and nice names", + "Use Pint's smart formatting capabilities", + "Show derivation source with user's terminology", + "Show what this makes ~1.0 in model units", + "Add any additional scales not in the standard order" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "list_derived_scales", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2528, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "List which scales were derived vs. directly specified.\n\nReturns a dictionary categorizing fundamental scales by how they were obtained:\neither directly from reference quantities or derived from compound quantities.\n\nReturns\n-------\ndict\n Dictionary with keys:\n - 'direct': List of (dimension, source) tuples for directly specified scales\n - 'derived': List of (dimension, derivation) tuples for derived scales\n - 'missing': List of standard dimensions that couldn't be determined\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> derivation = model.list_derived_scales()\n>>> derivation['direct'] # [('length', 'mantle_depth'), ('temperature', 'mantle_temperature')]\n>>> derivation['derived'] # [('time', 'length_scale \u00f7 plate_velocity'), ('mass', 'mantle_viscosity \u00d7 length_scale \u00d7 time_scale')]\n>>> derivation['missing'] # []", + "harvested_comments": [ + "[('length', 'mantle_depth'), ('temperature', 'mantle_temperature')]", + "[('time', 'length_scale \u00f7 plate_velocity'), ('mass', 'mantle_viscosity \u00d7 length_scale \u00d7 time_scale')]", + "Standard dimensions we expect to see", + "Categorize each scale found", + "Check if it's a direct mapping (starts with \"from \" and no operators)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "validate_dimensional_completeness", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2604, + "signature": "(self, required_dimensions = None) -> dict", + "parameters": [ + { + "name": "required_dimensions", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": "dict", + "existing_docstring": "Validate if reference quantities provide complete dimensional coverage.\n\nChecks whether the current reference quantities span enough dimensions\nto derive fundamental scales for all required dimensions, and provides\nsuggestions for completing under-determined systems.\n\nParameters\n----------\nrequired_dimensions : list, optional\n List of dimensions required (e.g., ['length', 'time', 'mass', 'temperature']).\n If None, uses the standard 4-dimension set.\n\nReturns\n-------\ndict\n Validation result containing:\n - 'status': 'complete', 'under_determined', or 'no_reference_quantities'\n - 'missing_dimensions': List of missing dimensions (if any)\n - 'suggestions': List of suggested reference quantities to add\n - 'derivable_dimensions': List of dimensions that can be derived\n - 'analysis': Human-readable analysis string\n\nExamples\n--------\n>>> # Under-determined system\n>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)\n>>> result = model.validate_dimensional_completeness()\n>>> result['status'] # 'under_determined'\n>>> result['missing_dimensions'] # ['time', 'mass', 'temperature']\n\n>>> # Complete system\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> result = model.validate_dimensional_completeness()\n>>> result['status'] # 'complete'", + "harvested_comments": [ + "Under-determined system", + "'under_determined'", + "['time', 'mass', 'temperature']", + "Complete system", + "Get what dimensions we can actually derive" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_scaling_mode", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2822, + "signature": "(self, mode = 'exact')", + "parameters": [ + { + "name": "mode", + "type_hint": null, + "default": "'exact'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the scaling mode for fundamental scales.\n\nParameters\n----------\nmode : {'exact', 'readable'}\n Scaling mode to use:\n - 'exact': Reference quantities scale to exactly 1.0 (default)\n - 'readable': Reference quantities scale to O(1) with nice round numbers\n\nExamples\n--------\n>>> # Default exact mode: reference quantities become exactly 1.0\n>>> model.set_scaling_mode('exact')\n>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)\n>>> model.to_model_units(2900*uw.units.km) # \u2192 UWQuantity(1.0, 'model_length_units')\n\n>>> # Readable mode: reference quantities become O(1) with nice scales\n>>> model.set_scaling_mode('readable')\n>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)\n>>> model.to_model_units(2900*uw.units.km) # \u2192 UWQuantity(2.9, 'model_length_units')\n>>> # Internal scale becomes 1000 km instead of 2900 km", + "harvested_comments": [ + "Default exact mode: reference quantities become exactly 1.0", + "\u2192 UWQuantity(1.0, 'model_length_units')", + "Readable mode: reference quantities become O(1) with nice scales", + "\u2192 UWQuantity(2.9, 'model_length_units')", + "Internal scale becomes 1000 km instead of 2900 km" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_scaling_mode", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 2855, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Get the current scaling mode.\n\nReturns\n-------\nstr\n Current scaling mode: 'exact' or 'readable'", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "show_optimal_units", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 3067, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Display the optimal units implied by this model's reference quantities.\n\nShows fundamental scalings (length, time, mass, temperature) derived from\nreference quantities and suggests optimal units for various physical quantities.", + "harvested_comments": [ + "# Optimal Units for Model: {self.name}\"]", + "Show fundamental scalings", + "## Fundamental Scalings\")", + "Derive optimal units for common quantities", + "## Recommended Units for This Problem\")" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "scale_to_physical", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 3398, + "signature": "(self, coordinates, dimension = 'length')", + "parameters": [ + { + "name": "coordinates", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dimension", + "type_hint": null, + "default": "'length'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert dimensionless model coordinates to physical units.\n\nThis method converts model-unit coordinate arrays (where the model domain\nis scaled to ~1.0) back to physical units using the model's fundamental scales.\n\nParameters\n----------\ncoordinates : array-like\n Dimensionless coordinate array in model units\ndimension : str, default 'length'\n Fundamental dimension to use for scaling ('length', 'time', 'mass', 'temperature')\n\nReturns\n-------\nUWQuantity\n Coordinates in physical units with appropriate units\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_length=1000*uw.units.km, ...)\n>>> mesh = uw.meshing.StructuredQuadBox(minCoords=(-3,-3), maxCoords=(3,3), ...)\n>>> # mesh.points are in model units (dimensionless, domain spans -3 to 3)\n>>> physical_coords = model.scale_to_physical(mesh.points)\n>>> # Result: coordinates in kilometers, spanning -3000 to 3000 km\n\nRaises\n------\nValueError\n If no reference quantities set or dimension not available", + "harvested_comments": [ + "mesh.points are in model units (dimensionless, domain spans -3 to 3)", + "Result: coordinates in kilometers, spanning -3000 to 3000 km", + "Get fundamental scales", + "Get the scale for this dimension", + "Import here to avoid circular imports" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "to_model_units", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 3459, + "signature": "(self, quantity)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Safely coerce any quantity to model units using smart protocol pattern.\n\nThis method is designed to be safe to call repeatedly and handles edge cases:\n1. Does nothing if model has no units (no reference quantities set)\n2. Does nothing if the quantity is already in model units\n3. Does nothing if the quantity is dimensionless\n4. Uses protocol pattern for extensible conversion\n\nParameters\n----------\nquantity : Any\n Quantity to convert (UWQuantity, Pint quantity, numeric, etc.)\n\nReturns\n-------\nUWQuantity or original quantity\n Converted quantity in model units, or original if no conversion needed", + "harvested_comments": [ + "1) Do nothing if there are no units (no reference quantities set)", + "2) Do nothing if quantity is already in model units", + "Check if it's a UWQuantity with model_units=True flag", + "3) Early check for dimensionless quantities - do nothing", + "Quick dimensionality check without full conversion" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "has_units_active", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 3711, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if the units system is active (reference quantities have been set).\n\nReturns\n-------\nbool\n True if reference quantities have been set, False otherwise.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "to_model_magnitude", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 3722, + "signature": "(self, quantity, expected_dimension = None)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "expected_dimension", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert quantity to model units and extract numerical magnitude.\n\n.. deprecated::\n This method is deprecated. Use ``uw.scaling.non_dimensionalise()`` instead\n for consistent unit conversion across the codebase. The global scaling\n function ensures all conversions use the same COEFFICIENTS dictionary\n that is synchronized with model reference quantities.\n\nThis is a convenience method that combines to_model_units() with magnitude\nextraction for internal use. It handles the common pattern of converting\nuser inputs (which may have units) to raw numerical values in model units\nfor internal computations.\n\nParameters\n----------\nquantity : Any\n Quantity to convert (UWQuantity, Pint quantity, numeric, tuple, etc.)\nexpected_dimension : str, optional\n Expected dimension like '[length]', '[time]', etc. If provided and\n the quantity is a plain number when units are active, a warning is\n issued to alert the user that they may have forgotten units.\n Common values: '[length]', '[time]', '[temperature]', '[mass]'\n\nReturns\n-------\nfloat, int, or array-like\n Raw numerical magnitude in model units (dimensionless)\n\nExamples\n--------\n>>> model.set_reference_quantities(length_scale=1000*uw.units.km)\n>>> coords_physical = [100*uw.units.km, 200*uw.units.km]\n>>> coords_model = model.to_model_magnitude(coords_physical)\n>>> coords_model\n[0.1, 0.2] # In model units (dimensionless)\n\n>>> # Also works with plain numbers (pass-through)\n>>> model.to_model_magnitude([0.1, 0.2])\n[0.1, 0.2]\n\n>>> # With expected_dimension, warns on plain numbers when units active\n>>> model.to_model_magnitude(6370, expected_dimension='[length]')\nUserWarning: Plain number 6370 passed for [length] parameter when units\nare active. If you intended physical units, use uw.quantity(6370, 'km').\nValue is being interpreted as 6370 model units.\n\n>>> # Works with time\n>>> dt_physical = 1000 * uw.units.year\n>>> dt_model = model.to_model_magnitude(dt_physical)\n\nNotes\n-----\nThis method is intended for internal use at API boundaries where user\ninputs need to be converted to model units for computations. The conversion\nrespects dimensional analysis and reference quantities.\n\nWhen `expected_dimension` is provided, this acts as a \"gatekeeper\" to catch\npotential bugs where users forget to attach units to their values.", + "harvested_comments": [ + "In model units (dimensionless)", + "Also works with plain numbers (pass-through)", + "With expected_dimension, warns on plain numbers when units active", + "Works with time", + "TODO: DEPRECATED - Use uw.scaling.non_dimensionalise() instead" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "from_model_magnitude", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 3857, + "signature": "(self, magnitude, dimensions)", + "parameters": [ + { + "name": "magnitude", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dimensions", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert numerical magnitude in model units back to physical quantity.\n\n.. deprecated::\n This method is deprecated. Use ``uw.scaling.dimensionalise()`` instead\n for consistent unit conversion across the codebase.\n\nThis is the inverse of to_model_magnitude() - it takes a raw numerical value\nin model units and wraps it with appropriate physical units based on the\ndimensional specification.\n\nParameters\n----------\nmagnitude : float, int, or array-like\n Numerical value(s) in model units (dimensionless)\ndimensions : str or Pint dimensionality\n Dimensional specification like '[length]', '[time]', '[length]/[time]', etc.\n Can also be a Pint dimensionality object from quantity.dimensionality\n\nReturns\n-------\nUWQuantity\n Physical quantity with appropriate units based on model reference scales\n\nExamples\n--------\n>>> model.set_reference_quantities(length_scale=1000*uw.units.km)\n>>> coords_model = np.array([0.1, 0.2]) # Model coordinates\n>>> coords_physical = model.from_model_magnitude(coords_model, '[length]')\n>>> coords_physical\n[100, 200] km # Back to physical units\n\n>>> # Works with composite dimensions\n>>> velocity_model = 1.0 # Model velocity magnitude\n>>> velocity_physical = model.from_model_magnitude(velocity_model, '[length]/[time]')\n\n>>> # If no reference quantities, returns SI base units\n>>> model_no_refs = uw.Model()\n>>> result = model_no_refs.from_model_magnitude(100, '[length]')\n>>> result\n100 m # SI base units when no scaling defined\n\nNotes\n-----\nThis method is intended for converting internal model-unit values back to\nphysical quantities when returning results to users. It ensures consistent\nunit handling across API boundaries.", + "harvested_comments": [ + "Model coordinates", + "Back to physical units", + "Works with composite dimensions", + "Model velocity magnitude", + "If no reference quantities, returns SI base units" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "to_dict", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4043, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "Export model configuration to dictionary for serialization.\n\nProvides enhanced serialization with SymPy expression conversion\nand PETSc state capture for complete reproducibility.\n\nReturns\n-------\ndict\n Model configuration dictionary suitable for JSON/YAML export", + "harvested_comments": [ + "Convert materials with SymPy expression handling" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "export_configuration", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4087, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "Alias for to_dict() for backward compatibility", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "from_dict", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4091, + "signature": "(self, config: Dict[str, Any])", + "parameters": [ + { + "name": "config", + "type_hint": "Dict[str, Any]", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Import model configuration from dictionary.\n\nHandles enhanced serialization format with SymPy expression reconstruction.\nNote: Only imports materials and metadata for now.\nMesh/variables/swarms must be recreated.\n\nParameters\n----------\nconfig : dict\n Configuration dictionary from to_dict() or YAML export", + "harvested_comments": [ + "Import materials with SymPy expression reconstruction", + "Reconstruct SymPy expression from string", + "Fall back to string" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "to_yaml", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4132, + "signature": "(self, file_path: Optional[str] = None) -> str", + "parameters": [ + { + "name": "file_path", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Export model configuration to YAML format.\n\nParameters\n----------\nfile_path : str, optional\n If provided, write YAML to this file path\n\nReturns\n-------\nstr\n YAML string representation of model configuration", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "from_yaml", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4155, + "signature": "(self, yaml_content: str = None, file_path: str = None)", + "parameters": [ + { + "name": "yaml_content", + "type_hint": "str", + "default": "None", + "description": "" + }, + { + "name": "file_path", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Import model configuration from YAML.\n\nParameters\n----------\nyaml_content : str, optional\n YAML string to parse\nfile_path : str, optional\n Path to YAML file to load", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "from_yaml_file", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4178, + "signature": "(cls, file_path: str) -> 'Model'", + "parameters": [ + { + "name": "file_path", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "'Model'", + "existing_docstring": "Create a new Model instance from YAML file.\n\nParameters\n----------\nfile_path : str\n Path to YAML configuration file\n\nReturns\n-------\nModel\n New model instance with loaded configuration", + "harvested_comments": [ + "Extract model-specific fields" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "capture_petsc_state", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4205, + "signature": "(self) -> Dict[str, str]", + "parameters": [], + "returns": "Dict[str, str]", + "existing_docstring": "Capture current PETSc options database state.\n\nReturns all PETSc options currently set, enabling complete\nreproducibility of simulation parameters.\n\nReturns\n-------\ndict\n Dictionary of PETSc option names and values", + "harvested_comments": [ + "Get all currently set PETSc options", + "PETSc doesn't provide a direct way to list all options,", + "but we can capture commonly used ones and any that were set", + "This is a simplified implementation - full implementation would", + "require more sophisticated PETSc option introspection" + ], + "status": "complete", + "needs": [], + "parent_class": "Model", + "is_public": true + }, + { + "name": "restore_petsc_state", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4264, + "signature": "(self, petsc_options: Optional[Dict[str, str]] = None)", + "parameters": [ + { + "name": "petsc_options", + "type_hint": "Optional[Dict[str, str]]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Restore PETSc options database from captured state.\n\nParameters\n----------\npetsc_options : dict, optional\n PETSc options to restore. If None, uses self.petsc_state", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "set_petsc_option", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4293, + "signature": "(self, option: str, value: str)", + "parameters": [ + { + "name": "option", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set a PETSc option and track it in model state.\n\nParameters\n----------\noption : str\n PETSc option name (without leading -)\nvalue : str\n Option value", + "harvested_comments": [ + "Track in model state" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4320, + "signature": "(self, verbose: int = 0, show_materials: bool = True, show_petsc: bool = False)", + "parameters": [ + { + "name": "verbose", + "type_hint": "int", + "default": "0", + "description": "" + }, + { + "name": "show_materials", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "show_petsc", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Display a concise summary of the model contents.\n\nParameters\n----------\nverbose : int, default 0\n Verbosity level:\n 0 = Basic summary\n 1 = Include variable details and material properties\n 2 = Include solver information and metadata\nshow_materials : bool, default True\n Whether to show materials summary\nshow_petsc : bool, default False\n Whether to show PETSc options (can be lengthy)\n\nExample\n-------\n>>> model.view() # Basic summary\n>>> model.view(verbose=1) # Detailed view\n>>> model.view(verbose=2, show_petsc=True) # Full details", + "harvested_comments": [ + "Basic summary", + "Detailed view", + "Full details", + "Build markdown content", + "Model: {self.name}\")" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4516, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Display comprehensive model information following the established view() pattern.\n\nShows model configuration, units setup, registered components, and provides\nguidance for setting up units if not configured.", + "harvested_comments": [ + "Build markdown content", + "# Model: {self.name}\"]", + "Model state and basic info", + "Mesh information", + "## Primary Mesh\")" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Model", + "is_public": true + }, + { + "name": "get_default_model", + "kind": "function", + "file": "src/underworld3/model.py", + "line": 4668, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Get or create the default model for this UW3 session.\n\nThe default model automatically registers all meshes, swarms, variables,\nand solvers created during the session, enabling serialization and\nmodel orchestration without explicit user interaction.\n\nReturns\n-------\nModel\n The default model instance for this session\n\nExample\n-------\n>>> import underworld3 as uw\n>>> model = uw.get_default_model()\n>>> print(model) # See all registered objects\n>>> config = model.to_dict() # Serialize model", + "harvested_comments": [ + "See all registered objects", + "Serialize model" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "reset_default_model", + "kind": "function", + "file": "src/underworld3/model.py", + "line": 4694, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Reset the default model to a fresh instance.\n\nUseful for testing or starting a new simulation in an interactive session.\nAll previously registered objects will be orphaned from the model registry.\n\nAlso resets global state including strict units mode to default (ON).\n\nReturns\n-------\nModel\n New default model instance\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.reset_default_model() # Start fresh", + "harvested_comments": [ + "Start fresh", + "Reset strict units mode to default (ON)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "to_petsc_options", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4763, + "signature": "(self) -> Dict[str, str]", + "parameters": [], + "returns": "Dict[str, str]", + "existing_docstring": "Convert configuration to PETSc options dictionary.\n\nReturns\n-------\ndict\n PETSc options suitable for setting via Model.set_petsc_option()", + "harvested_comments": [ + "Stokes solver options", + "Advection-diffusion options" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ThermalConvectionConfig", + "is_public": true + }, + { + "name": "to_materials_dict", + "kind": "method", + "file": "src/underworld3/model.py", + "line": 4798, + "signature": "(self) -> Dict[str, Dict[str, Any]]", + "parameters": [], + "returns": "Dict[str, Dict[str, Any]]", + "existing_docstring": "Convert configuration to materials dictionary format.\n\nReturns\n-------\ndict\n Materials dictionary suitable for Model.materials", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ThermalConvectionConfig", + "is_public": true + }, + { + "name": "create_model", + "kind": "function", + "file": "src/underworld3/model.py", + "line": 4817, + "signature": "(name: Optional[str] = None) -> Model", + "parameters": [ + { + "name": "name", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + } + ], + "returns": "Model", + "existing_docstring": "Create a new Model instance", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_thermal_convection_model", + "kind": "function", + "file": "src/underworld3/model.py", + "line": 4822, + "signature": "(config: ThermalConvectionConfig, name: str = 'thermal_convection') -> Model", + "parameters": [ + { + "name": "config", + "type_hint": "ThermalConvectionConfig", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "'thermal_convection'", + "description": "" + } + ], + "returns": "Model", + "existing_docstring": "Create a Model instance configured for thermal convection.\n\nDemonstrates integration between specialized configs and Model infrastructure.\n\nParameters\n----------\nconfig : ThermalConvectionConfig\n Configuration object with all simulation parameters\nname : str\n Model name\n\nReturns\n-------\nModel\n Configured model ready for thermal convection simulation", + "harvested_comments": [ + "Set materials from config", + "Set PETSc options from config", + "Store config in metadata for reproducibility" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "barrier", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 50, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Creates an MPI barrier. All processes wait here for others to catch up.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "selective_ranks", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 160, + "signature": "(ranks)", + "parameters": [ + { + "name": "ranks", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Execute code only on selected ranks, with collective operation detection.\n\nThis context manager allows you to selectively execute code on specific MPI ranks\nwhile protecting against deadlocks from collective operations.\n\nArgs:\n ranks: Which ranks should execute the code block. Can be:\n - int: Single rank (e.g., 0)\n - slice: Range of ranks (e.g., slice(0, 4))\n - list/tuple: Specific ranks (e.g., [0, 3, 7])\n - str: Named patterns ('all', 'first', 'last', 'even', 'odd', '10%')\n - callable: Function taking rank and returning bool\n - numpy array: Boolean mask or integer indices\n\nRaises:\n CollectiveOperationError: If a collective operation is detected within\n the selective execution block (would cause deadlock)\n\nExample:\n >>> with uw.mpi.selective_ranks(0):\n ... import matplotlib.pyplot as plt\n ... plt.plot(x, y)\n ... plt.savefig(\"output.png\")", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "collective_operation", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 215, + "signature": "(func)", + "parameters": [ + { + "name": "func", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Decorator to mark a function as a collective operation.\n\nCollective operations must be called on ALL MPI ranks. If called inside\na selective_ranks() context where not all ranks execute, raises CollectiveOperationError.\n\nExample:\n >>> @collective_operation\n ... def compute_global_stats(self):\n ... # This requires all ranks to participate\n ... return self.vec.norm()", + "harvested_comments": [ + "This requires all ranks to participate", + "Check if all ranks are executing", + "Not all ranks will execute - this is a collective operation error", + "All ranks execute\\n\"" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "pprint", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 265, + "signature": "(*args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Parallel-safe print that works as a drop-in replacement for print().\n\nThis function ensures all ranks execute any collective operations in the arguments,\nbut only selected ranks actually print output. This prevents deadlocks from\ncollective operations inside rank conditionals.\n\nArgs:\n *args: Arguments to print (same as standard print())\n proc: Which ranks should print. Can be:\n - int: Single rank (e.g., 0) [default: 0]\n - slice: Range of ranks (e.g., slice(0, 4))\n - list/tuple: Specific ranks (e.g., [0, 3, 7])\n - str: Named patterns ('all', 'first', 'last', 'even', 'odd', '10%')\n - callable: Function taking rank and returning bool\n - numpy array: Boolean mask or integer indices\n prefix: If True, prefix output with rank number. If None (default),\n automatically enables in parallel (size > 1) and disables in serial.\n clean_display: If True, filter out SymPy uniqueness strings for cleaner display (default: True)\n flush: If True, forcibly flush the stream (default: False, same as print())\n **kwargs: Additional keyword arguments passed to print() (sep, end, file)\n\nExample:\n >>> uw.pprint(f\"Global max: {var.stats()['max']}\") # Only rank 0 prints\n Global max: 42.5\n\n >>> # In parallel, automatic prefix\n >>> uw.pprint(f\"Local max: {var.data.max()}\", proc=slice(0, 4))\n [0] Local max: 12.3\n [1] Local max: 15.7\n [2] Local max: 9.8\n [3] Local max: 11.2\n\n >>> uw.pprint(f\"Expression: {expr}\") # Automatically cleans symbols\n Expression: T(x,y)", + "harvested_comments": [ + "Only rank 0 prints", + "In parallel, automatic prefix", + "Automatically cleans symbols", + "Auto-detect prefix: True in parallel, False in serial", + "Clean up display strings by filtering out SymPy uniqueness patterns" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "pprint_old", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 341, + "signature": "(ranks, *args, **kwargs)", + "parameters": [ + { + "name": "ranks", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Legacy pprint interface (deprecated). Use pprint() with proc= parameter instead.\n\nThis function maintains backward compatibility for existing code.", + "harvested_comments": [ + "Convert to new interface" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "validate", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 65, + "signature": "(self, value: Any) -> bool", + "parameters": [ + { + "name": "value", + "type_hint": "Any", + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Validate a parameter value against this definition.\n\nReturns:\n--------\nbool\n True if value is valid, raises ValueError if not", + "harvested_comments": [ + "Type validation", + "Custom validation" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterDefinition", + "is_public": true + }, + { + "name": "define_parameter", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 148, + "signature": "(self, name: str, ptype: ParameterType, default: Any = None, bounds: Optional[tuple] = None, choices: Optional[list] = None, description: str = '', units: str = '', validator: Optional[Callable] = None, dependencies: Optional[List[str]] = None) -> None", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "ptype", + "type_hint": "ParameterType", + "default": null, + "description": "" + }, + { + "name": "default", + "type_hint": "Any", + "default": "None", + "description": "" + }, + { + "name": "bounds", + "type_hint": "Optional[tuple]", + "default": "None", + "description": "" + }, + { + "name": "choices", + "type_hint": "Optional[list]", + "default": "None", + "description": "" + }, + { + "name": "description", + "type_hint": "str", + "default": "''", + "description": "" + }, + { + "name": "units", + "type_hint": "str", + "default": "''", + "description": "" + }, + { + "name": "validator", + "type_hint": "Optional[Callable]", + "default": "None", + "description": "" + }, + { + "name": "dependencies", + "type_hint": "Optional[List[str]]", + "default": "None", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Define a new parameter with validation rules.\n\nParameters:\n-----------\nname : str\n Parameter path (e.g., 'material.viscosity')\nptype : ParameterType\n Parameter type for validation\ndefault : Any\n Default value\nbounds : tuple, optional\n (min, max) bounds for numerical parameters\nchoices : list, optional\n Valid choices for enum parameters\ndescription : str\n Human-readable description\nunits : str\n Physical units\nvalidator : callable, optional\n Custom validation function\ndependencies : list, optional\n Parameters that depend on this one", + "harvested_comments": [ + "Set default value if provided" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "set_parameter", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 202, + "signature": "(self, name: str, value: Any, validate: bool = True) -> None", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": "Any", + "default": null, + "description": "" + }, + { + "name": "validate", + "type_hint": "bool", + "default": "True", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Set a parameter value with validation and dependency updates.\n\nParameters:\n-----------\nname : str\n Parameter path\nvalue : Any\n New parameter value\nvalidate : bool\n Whether to validate the value", + "harvested_comments": [ + "Get definition for validation", + "Validate value", + "Store old value for history", + "Set new value", + "Record change in history" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "get_parameter", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 260, + "signature": "(self, name: str, default = None) -> Any", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "default", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Get a parameter value by name", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "has_parameter", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 264, + "signature": "(self, name: str) -> bool", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if a parameter is defined", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "list_parameters", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 268, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "List all parameter names and current values", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "get_definition", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 272, + "signature": "(self, name: str) -> Optional[ParameterDefinition]", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "Optional[ParameterDefinition]", + "existing_docstring": "Get parameter definition by name", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "add_callback", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 276, + "signature": "(self, name: str, callback: Callable) -> None", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "callback", + "type_hint": "Callable", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Add a callback function to be called when parameter changes.\n\nParameters:\n-----------\nname : str\n Parameter path\ncallback : callable\n Function called as callback(param_name, new_value, old_value)", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "export_config", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 291, + "signature": "(self) -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "Export current parameter configuration", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "import_config", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 308, + "signature": "(self, config: Dict[str, Any], validate: bool = True) -> None", + "parameters": [ + { + "name": "config", + "type_hint": "Dict[str, Any]", + "default": null, + "description": "" + }, + { + "name": "validate", + "type_hint": "bool", + "default": "True", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Import parameter configuration from exported dict", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": true + }, + { + "name": "define_thermal_convection_parameters", + "kind": "function", + "file": "src/underworld3/parameters.py", + "line": 322, + "signature": "(registry: ParameterRegistry) -> None", + "parameters": [ + { + "name": "registry", + "type_hint": "ParameterRegistry", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Define standard parameters for thermal convection models", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "define_stokes_flow_parameters", + "kind": "function", + "file": "src/underworld3/parameters.py", + "line": 379, + "signature": "(registry: ParameterRegistry) -> None", + "parameters": [ + { + "name": "registry", + "type_hint": "ParameterRegistry", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Define standard parameters for Stokes flow models", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "get_coefficients", + "kind": "function", + "file": "src/underworld3/scaling/_scaling.py", + "line": 83, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Returns the global scaling dictionary.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "non_dimensionalise", + "kind": "function", + "file": "src/underworld3/scaling/_scaling.py", + "line": 98, + "signature": "(dimValue)", + "parameters": [ + { + "name": "dimValue", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Non-dimensionalize (scale) provided quantity.\n\nThis function uses pint to perform a dimension analysis and\nreturn a value scaled according to a set of scaling coefficients.\n\nThe function is idempotent: calling it multiple times with the same\ninput returns the same result. Non-dimensional arrays (plain numpy arrays,\nor UnitAwareArray with units=None or units='dimensionless') are returned\nunchanged, as they are already in non-dimensional form.\n\nParameters\n----------\ndimValue : pint.Quantity, UnitAwareArray, ndarray, or float\n A value to be non-dimensionalized.\n - pint.Quantity: Converted to non-dimensional value\n - UnitAwareArray with no units or dimensionless: Returned as-is (already ND)\n - Plain numpy array: Returned as-is (already ND)\n - Plain number (int/float): Returned as-is (assumed already ND)\n\nReturns\n-------\nfloat or ndarray\n The non-dimensional value.\n\nExample\n-------\n\n>>> import underworld as uw\n>>> u = uw.scaling.units\n\n>>> # Characteristic values of the system\n>>> half_rate = 0.5 * u.centimeter / u.year\n>>> model_height = 600e3 * u.meter\n>>> refViscosity = 1e24 * u.pascal * u.second\n>>> surfaceTemp = 0. * u.kelvin\n>>> baseModelTemp = 1330. * u.kelvin\n>>> baseCrustTemp = 550. * u.kelvin\n\n>>> KL_meters = model_height\n>>> KT_seconds = KL_meters / half_rate\n>>> KM_kilograms = refViscosity * KL_meters * KT_seconds\n>>> Kt_degrees = (baseModelTemp - surfaceTemp)\n>>> K_substance = 1. * u.mole\n\n>>> scaling_coefficients = uw.scaling.get_coefficients()\n>>> scaling_coefficients[\"[time]\"] = KT_seconds\n>>> scaling_coefficients[\"[length]\"] = KL_meters\n>>> scaling_coefficients[\"[mass]\"] = KM_kilograms\n>>> scaling_coefficients[\"[temperature]\"] = Kt_degrees\n>>> scaling_coefficients[\"[substance]\"] -= K_substance\n\n>>> # Get a scaled value:\n>>> gravity = uw.scaling.non_dimensionalise(9.81 * u.meter / u.second**2)", + "harvested_comments": [ + "Characteristic values of the system", + "Get a scaled value:", + "IDEMPOTENCY CHECK: Return early if already non-dimensional", + "Non-dimensional values are:", + "1. Plain numpy arrays (no unit information)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "dimensionalise", + "kind": "function", + "file": "src/underworld3/scaling/_scaling.py", + "line": 279, + "signature": "(value, units)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Dimensionalise a value.\n\nParameters\n----------\nvalue : float, int\n The value to be assigned units.\nunits : pint units\n The units to be assigned.\n\nReturns\n-------\npint quantity: dimensionalised value.\n\nExample\n-------\n>>> import underworld as uw\n>>> A = uw.scaling.dimensionalise(1.0, u.metre)", + "harvested_comments": [ + "Check that the scaling parameters have the correct dimensions", + "Get dimensionality", + "tempVar = value.copy()", + "tempVar.data[...] = (value.data[...] * factor).to(units).magnitude", + "return tempVar" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "ndargs", + "kind": "function", + "file": "src/underworld3/scaling/_scaling.py", + "line": 352, + "signature": "(f)", + "parameters": [ + { + "name": "f", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Decorator used to non-dimensionalise the arguments of a function", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "ensure_lower", + "kind": "function", + "file": "src/underworld3/scaling/_utils.py", + "line": 15, + "signature": "(maybe_str)", + "parameters": [ + { + "name": "maybe_str", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "dict keys can be any hashable object - only call lower if str", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 366, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return the units associated with this variable.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "units", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 371, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the units for this variable.", + "harvested_comments": [ + "Convert string units to Pint Unit objects for consistency" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 380, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if this variable has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "use_legacy_array", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 987, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: Array interface is now unified using NDArray_With_Callback", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "use_enhanced_array", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 991, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: Array interface is now unified using NDArray_With_Callback", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "sync_disabled", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 995, + "signature": "(self, description = 'batch operation')", + "parameters": [ + { + "name": "description", + "type_hint": null, + "default": "'batch operation'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Context manager to disable automatic synchronization for batch operations.\nNow uses NDArray_With_Callback's delay_callback mechanism.\n\nParameters\n----------\ndescription : str\n Description of the batch operation for debugging", + "harvested_comments": [ + "Use NDArray_With_Callback's built-in delay mechanism" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "pack_uw_data_to_petsc", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1285, + "signature": "(self, data_array, sync = None)", + "parameters": [ + { + "name": "data_array", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sync", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Enhanced pack method that directly accesses PETSc field without access() context.\nDesigned for the new swarmVariable.array interface.\n\nParameters\n----------\ndata_array : numpy.ndarray\n Array data to pack into PETSc field\nsync : deprecated\n Never had an effect; deprecated (one DeprecationWarning if passed).", + "harvested_comments": [ + "Direct PETSc field access without context manager", + "Pack data using same layout as original method", + "Increment variable state to track changes", + "Update the proxy mesh variable if one exists (for integral calculations)", + "Always restore the field" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "unpack_uw_data_from_petsc", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1344, + "signature": "(self, squeeze = True, sync = None)", + "parameters": [ + { + "name": "squeeze", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "sync", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Enhanced unpack method that directly accesses PETSc field without access() context.\nDesigned for the new swarmVariable.array interface.\n\nParameters\n----------\nsqueeze : bool\n Whether to squeeze singleton dimensions (default True)\nsync : deprecated\n Never had an effect; deprecated (one DeprecationWarning if passed).", + "harvested_comments": [ + "Direct PETSc field access without context manager", + "Unpack data using same layout as original method", + "Always restore the field" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "pack_raw_data_to_petsc", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1381, + "signature": "(self, data_array, sync = None)", + "parameters": [ + { + "name": "data_array", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sync", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pack data array to PETSc using traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\ndata_array : numpy.ndarray\n Array data in traditional flat format (-1, num_components)\nsync : deprecated\n Never had an effect; deprecated (one DeprecationWarning if passed).", + "harvested_comments": [ + "Convert to expected shape: (-1, num_components)", + "Direct PETSc field access without context manager", + "Direct assignment in traditional flat format", + "Increment variable state to track changes", + "Update the proxy mesh variable if one exists (for integral calculations)" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "unpack_raw_data_from_petsc", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1422, + "signature": "(self, squeeze = True, sync = None)", + "parameters": [ + { + "name": "squeeze", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "sync", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Unpack data from PETSc in traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\nsqueeze : bool\n Whether to remove singleton dimensions (default True)\nsync : deprecated\n Never had an effect; deprecated (one DeprecationWarning if passed).\n\nReturns\n-------\nnumpy.ndarray\n Array data in traditional flat format (-1, num_components)", + "harvested_comments": [ + "Check if swarm has any particles before accessing field", + "Swarm not populated yet, return empty array", + "Direct PETSc field access without context manager", + "Field not properly initialized, restore and return empty array", + "Return data in traditional flat format" + ], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "rbf_interpolate", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1492, + "signature": "(self, new_coords, verbose = False, nnn = None)", + "parameters": [ + { + "name": "new_coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "nnn", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Radial basis function interpolation of particle data to arbitrary points.\n\nUses inverse-distance weighting to interpolate particle values\nto new coordinate locations.\n\nParameters\n----------\nnew_coords : numpy.ndarray\n Target coordinates of shape (N, dim) to interpolate to.\nverbose : bool, default=False\n Print diagnostic information during interpolation.\nnnn : int, optional\n Number of nearest neighbors to use. Defaults to ``mesh.dim + 1``.\n\nReturns\n-------\nnumpy.ndarray\n Interpolated values at the target coordinates.", + "harvested_comments": [ + "An inverse-distance mapping is quite robust here ... as long", + "as we take care of the case where some nodes coincide (likely if used with mesh2mesh)", + "We try to eliminate contributions from recently remeshed particles", + "Get data directly from PETSc to avoid circular callback dependencies", + "What to do if there are no particles: never SILENTLY return zeros" + ], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "swarm", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 1554, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "The swarm this variable belongs to (accessed via weak reference).\nRaises RuntimeError if the swarm has been garbage collected.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "old_data", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 1571, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "TESTING: Original data property implementation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 1578, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Canonical data storage in flat format for internal operations.\n\nReturns particle data in shape ``(-1, num_components)`` regardless of\nvariable type. Values are always **non-dimensional** (no unit conversion).\n\nThis property handles PETSc synchronization. The ``.array`` property is\na view of this with shape conversion.\n\nWhen to Use\n-----------\n- **Variable-to-variable transfers**: Copying data between swarm variables\n avoids redundant unit conversions\n- **Low-level operations**: Direct access to particle data\n- **Backward compatibility**: Existing code using flat format\n\nFor general user access, prefer ``.array`` which provides a structured shape.\n\nReturns\n-------\nNDArray_With_Callback\n Array with shape ``(-1, num_components)`` with automatic PETSc sync.\n\nExamples\n--------\n>>> # Efficient variable-to-variable copy\n>>> new_material.data[...] = old_material.data[...]\n\n>>> # Check data shape\n>>> scalar_var.data.shape # (n_particles, 1)\n>>> vector_var.data.shape # (n_particles, dim)\n\nSee Also\n--------\narray : Structured format ``(N, a, b)`` for general access.\nsym : Symbolic representation for equations.", + "harvested_comments": [ + "Efficient variable-to-variable copy", + "Check data shape", + "(n_particles, 1)", + "(n_particles, dim)", + "Cache and reuse canonical data object to avoid field access conflicts" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "array", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 1625, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Primary interface for reading and writing particle data.\n\nReturns a structured array view with shape ``(N, a, b)`` where ``N`` is\nthe number of particles and ``(a, b)`` depends on the variable type:\n\n- Scalar: ``(N, 1, 1)``\n- Vector: ``(N, 1, dim)``\n- Tensor: ``(N, dim, dim)``\n\nReturns\n-------\nNDArray\n Array view that delegates changes back to canonical storage.\n\nExamples\n--------\n>>> # Scalar field initialization\n>>> material_property.array[:, 0, 0] = 1e21 # Set viscosity\n\n>>> # Vector field\n>>> velocity.array[:, 0, 0] = vx_values # x-component\n>>> velocity.array[:, 0, 1] = vy_values # y-component\n\n>>> # Reading values\n>>> max_visc = material_property.array[:, 0, 0].max()\n\nNotes\n-----\nThis property is a view of the canonical ``.data`` property with\nautomatic shape conversion. All modifications trigger proxy variable\nupdates for mesh interpolation.\n\nSee Also\n--------\ndata : Flat format ``(-1, components)`` for variable-to-variable transfers.\nsym : Symbolic representation for use in equations.", + "harvested_comments": [ + "Scalar field initialization", + "Set viscosity", + "Vector field", + "x-component", + "y-component" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "array", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1667, + "signature": "(self, array_value)", + "parameters": [ + { + "name": "array_value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set variable data through canonical data property with format conversion.", + "harvested_comments": [ + "Simple case: reshape array format (N,a,b) to canonical format (N,components)", + "Complex case: use pack operations for tensor layout conversion", + "Assign to canonical data property (triggers PETSc sync)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "sym", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 1682, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Symbolic representation for use in equations.\n\nReturns the symbolic expression from the proxy mesh variable,\nwhich can be used in SymPy expressions for constitutive models,\nboundary conditions, etc.\n\nReturns\n-------\nsympy.Matrix\n Symbolic matrix expression.\n\nNotes\n-----\nThe proxy is automatically updated if particle data has changed.", + "harvested_comments": [ + "Ensure proxy is up to date before returning symbolic representation" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "sym_1d", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 1703, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Flattened symbolic representation.\n\nReturns the symbolic expression as a 1D (column) vector form,\nuseful for Voigt notation in tensor calculations.\n\nReturns\n-------\nsympy.Matrix\n Flattened symbolic expression.", + "harvested_comments": [ + "Ensure proxy is up to date before returning symbolic representation" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "global_max", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1724, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Maximum value across all MPI ranks.\n\nFinds the maximum value of the particle property across all processors.\nUseful for finding extreme values in particle swarm data.\n\nParameters\n----------\naxis : None, int, or tuple of ints, optional\n Axis or axes along which to operate. By default, flattened input is used.\nout : None, optional\n Alternative output array (not supported, kept for API compatibility).\nkeepdims : bool, optional\n If True, reduced axes are left as dimensions with size one.\n\nReturns\n-------\nUWQuantity or scalar\n Maximum value with units preserved (if variable has units).\n\nExamples\n--------\n>>> max_temp = temperature_swarm.global_max()\n>>> print(f\"Maximum temperature: {max_temp}\")\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.", + "harvested_comments": [ + "Wrap data in UnitAwareArray to use its global_max implementation" + ], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "global_min", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1762, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Minimum value across all MPI ranks.\n\nFinds the minimum value of the particle property across all processors.\nUseful for finding extreme values in particle swarm data.\n\nParameters\n----------\naxis : None, int, or tuple of ints, optional\n Axis or axes along which to operate. By default, flattened input is used.\nout : None, optional\n Alternative output array (not supported, kept for API compatibility).\nkeepdims : bool, optional\n If True, reduced axes are left as dimensions with size one.\n\nReturns\n-------\nUWQuantity or scalar\n Minimum value with units preserved (if variable has units).\n\nExamples\n--------\n>>> min_pressure = pressure_swarm.global_min()\n>>> print(f\"Minimum pressure: {min_pressure}\")\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "global_sum", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1799, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Sum of values across all MPI ranks.\n\nComputes the sum of particle property values across all processors.\n\nParameters\n----------\naxis : None, int, or tuple of ints, optional\n Axis or axes along which to operate. By default, flattened input is used.\nout : None, optional\n Alternative output array (not supported, kept for API compatibility).\nkeepdims : bool, optional\n If True, reduced axes are left as dimensions with size one.\n\nReturns\n-------\nUWQuantity or scalar\n Sum with units preserved (if variable has units).\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.\n\nWarning: This sum is NOT a physical domain-integrated quantity because\nparticles are non-uniformly distributed. For domain integration, use\nthe proxy mesh variable with uw.maths.Integral().", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "global_norm", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1834, + "signature": "(self, ord = None)", + "parameters": [ + { + "name": "ord", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "L2 norm (Frobenius norm) across all MPI ranks.\n\nComputes the L2 norm of particle property values: sqrt(sum(x**2))\nacross all processors.\n\nParameters\n----------\nord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional\n Order of the norm (default: None = 2-norm)\n\nReturns\n-------\nUWQuantity or scalar\n L2 norm with units preserved (if variable has units).\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.\n\nFor vectors, computes the Frobenius norm treating the array as flattened.\n\nWarning: This norm is NOT a physical domain-integrated quantity because\nparticles are non-uniformly distributed.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "global_size", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1867, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Total particle count across all MPI ranks.\n\nReturns the total number of particles across all processors.\nUseful for population monitoring and load balancing diagnostics.\n\nReturns\n-------\nint\n Total number of particles across all ranks.\n\nExamples\n--------\n>>> total_particles = swarm_var.global_size()\n>>> local_particles = swarm_var.data.shape[0]\n>>> print(f\"Rank has {local_particles} of {total_particles} particles\")\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "save", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1896, + "signature": "(self, filename: int, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential = False)", + "parameters": [ + { + "name": "filename", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "compression", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "compressionType", + "type_hint": "Optional[str]", + "default": "'gzip'", + "description": "" + }, + { + "name": "force_sequential", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Save the swarm variable to a h5 file.\n\nParameters\n----------\nfilename :\n The filename of the swarm variable to save to disk.\ncompression :\n Add compression to the h5 files (saves space but increases write times with increasing no. of processors)\ncompressionType :\n Type of compression to use, 'gzip' and 'lzf' supported. 'gzip' is default. Compression also needs to be set to 'True'.\n\nforce_sequential : activate the serial version of hdf5", + "harvested_comments": [ + "BUGFIX(#151): the previous parallel path called", + "h5f.create_dataset(\"data\", data=self.data[:])", + "collectively, but each rank passed its own local-sized array.", + "In parallel HDF5 every rank must specify the *same* dataset", + "shape on a collective create_dataset; passing different shapes" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SwarmVariable", + "is_public": true + }, + { + "name": "sym", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 2301, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Lazy evaluation of symbolic mask array.\n\nOnly updates proxy variables when they're actually needed (when sym is accessed)\nand only if the proxy variables are marked as stale due to data changes.\nThis avoids expensive RBF interpolation during data assignment operations.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "IndexSwarmVariable", + "is_public": true + }, + { + "name": "sym_1d", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 2313, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "One-dimensional symbolic mask array (alias for :attr:`sym`).\n\nReturns the same symbolic mask array as :attr:`sym`, provided for API\ncompatibility with other variable types that distinguish between\nmulti-dimensional and flattened representations.\n\nReturns\n-------\nsympy.Matrix\n Symbolic mask array of shape (indices, 1).\n\nSee Also\n--------\nsym : Primary symbolic mask array access.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "IndexSwarmVariable", + "is_public": true + }, + { + "name": "createMask", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2337, + "signature": "(self, funcsList)", + "parameters": [ + { + "name": "funcsList", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a material-weighted symbolic expression from per-material values.\n\nThis method creates a SymPy expression that combines multiple material\nproperties using the index variable's symbolic masks. The result can be\nused directly in Underworld's solver equations.\n\nParameters\n----------\nfuncsList : list or tuple\n List of values or symbolic expressions, one per material index.\n Length must equal :attr:`indices`.\n\nReturns\n-------\nsympy.Basic\n Symbolic expression: ``sum(funcsList[i] * mask[i] for i in indices)``.\n\nRaises\n------\nRuntimeError\n If ``funcsList`` is not a list/tuple or has wrong length.\n\nExamples\n--------\n>>> # Define viscosity per material\n>>> viscosity = material.createMask([1e21, 1e20, 1e22]) # 3 materials\n>>> # Use in solver\n>>> stokes.constitutive_model.viscosity = viscosity\n\nSee Also\n--------\nvisMask : Create visualization mask showing material indices.", + "harvested_comments": [ + "Define viscosity per material", + "3 materials", + "Use in solver" + ], + "status": "complete", + "needs": [], + "parent_class": "IndexSwarmVariable", + "is_public": true + }, + { + "name": "viewMask", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2385, + "signature": "(self, expr)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Decompose a masked expression into per-material components.\n\n.. note::\n This method is not yet implemented. Currently returns None.\n\nTakes a symbolic expression created by :meth:`createMask` and extracts\nthe individual material-specific components.\n\nParameters\n----------\nexpr : sympy.Basic\n A masked symbolic expression created by :meth:`createMask`.\n\nReturns\n-------\nlist or None\n List of symbolic expressions, one per material index.\n Currently returns None (not implemented).\n\nSee Also\n--------\ncreateMask : Create a masked expression from per-material values.", + "harvested_comments": [ + "TODO: Implement decomposition of masked expressions", + "output = []", + "for i in range(self.indices):", + "for j in range(self.indices):", + "if i == j: pass" + ], + "status": "complete", + "needs": [], + "parent_class": "IndexSwarmVariable", + "is_public": true + }, + { + "name": "visMask", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2420, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Create a visualization mask showing material indices.\n\nReturns a symbolic expression where each material region shows its\nindex value (0, 1, 2, ...). Useful for visualization and debugging\nof material distributions.\n\nReturns\n-------\nsympy.Basic\n Symbolic expression evaluating to material index at each point.\n\nExamples\n--------\n>>> vis_field = material.visMask()\n>>> values = uw.function.evaluate(vis_field, swarm.data)\n>>> # values[i] gives material index at particle i\n\nSee Also\n--------\ncreateMask : Create arbitrary material-weighted expressions.", + "harvested_comments": [ + "values[i] gives material index at particle i" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "IndexSwarmVariable", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2445, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Show information on IndexSwarmVariable", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "IndexSwarmVariable", + "is_public": true + }, + { + "name": "mesh", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3009, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "The mesh this swarm operates on", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "mesh", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3017, + "signature": "(self, new_mesh)", + "parameters": [ + { + "name": "new_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Assign swarm to a new mesh with dimensional validation and proxy updates.\n\nParameters\n----------\nnew_mesh : uw.discretisation.Mesh\n New mesh to assign this swarm to\n\nRaises\n------\nValueError\n If new mesh has incompatible dimensions", + "harvested_comments": [ + "Register new mesh with model", + "Check if swarm is already compatible with target mesh", + "Dimensions match, check if proxy variables need updating", + "No change needed", + "Use swarm's current dimensions for validation (not model.mesh which may have been auto-updated)" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "local_size", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3092, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Number of particles on this MPI rank.\n\nReturns\n-------\nint\n Local particle count.\n\nSee Also\n--------\ndm.getLocalSize : Underlying PETSc method.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3109, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Particle coordinates (alias for :attr:`points`).\n\n.. deprecated:: 0.99.0\n Use direct DM field access for particle coordinates.\n\nReturns\n-------\nnumpy.ndarray\n Particle coordinate array of shape ``(n_particles, dim)``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "points", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3123, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Swarm particle coordinates in physical units.\n\n.. deprecated:: 0.99.0\n Use swarm variables or direct DM access instead.\n ``swarm.points`` is being deprecated.\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from internal model coordinates\nto physical coordinates for user access.\n\nWhen the mesh has coordinate units specified, returns a unit-aware array.\n\nReturns:\n numpy.ndarray or UnitAwareArray: Particle coordinates (with units if mesh.units is set)", + "harvested_comments": [ + "Check for mesh coordinate changes and trigger migration if needed", + "Mesh coordinates have changed, force migration to update swarm", + "Update our mesh version to match", + "Get current coordinate data from PETSc (these are in model coordinates)", + "Apply scaling to convert model coordinates to physical coordinates" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "points", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3234, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set swarm particle coordinates from physical units.\n\n.. deprecated:: 0.99.0\n Use swarm variables or direct DM access instead.\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from physical coordinates\nto internal model coordinates for PETSc storage.\n\nArgs:\n value (numpy.ndarray): Particle coordinates in physical units", + "harvested_comments": [ + "Apply inverse scaling to convert physical coordinates to model coordinates", + "Update the cached NDArray (triggers callback) - use physical coordinates for cache", + "Update PETSc DM field directly with model coordinates for immediate consistency" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "coords", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3293, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Swarm particle coordinates in physical units.\n\nThis is the primary public interface for accessing particle coordinates.\nCoordinates are automatically converted from internal model units to\nphysical units based on the model's reference quantities.\n\nReturns\n-------\nUWQuantity or numpy.ndarray\n Particle coordinates in physical units with shape (n_particles, dim).\n If model has reference quantities, returns UWQuantity with appropriate\n length units. Otherwise returns plain array.\n\nNotes\n-----\n- Coordinates are converted from model units to physical units automatically\n- For internal use with model units, access `swarm._particle_coordinates.data`\n- Setting coordinates accepts either physical units or plain numbers\n\nExamples\n--------\n>>> coords_physical = swarm.coords # Get physical coordinates\n>>> swarm.coords = new_coords_with_units # Set from physical units\n\nSee Also\n--------\nswarm.units : Get the unit specification for coordinates", + "harvested_comments": [ + "Get physical coordinates", + "Set from physical units", + "Get internal model-unit coordinates", + "Convert to physical units if reference quantities are set", + "If no reference quantities, return raw coordinates (model units = physical units)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "coords", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3343, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set swarm particle coordinates from physical units.\n\nAccepts coordinates with units or plain numbers. If units are provided,\nthey are converted to model units automatically. If plain numbers are\nprovided, they are assumed to be in the correct unit system.\n\nParameters\n----------\nvalue : array-like or UWQuantity\n New coordinates. Can be:\n - Array with units (e.g., values * uw.units.km)\n - Plain array (assumed to be in model units or physical units depending on context)", + "harvested_comments": [ + "Convert physical \u2192 non-dimensional units", + "Set internal coordinates" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3367, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Unit specification for swarm coordinates.\n\nReturns the physical unit string for coordinates based on the model's\nreference quantities. This indicates what units the coordinates are in\nwhen accessed via the `coords` property.\n\nReturns\n-------\nstr or None\n Unit string for coordinates (e.g., 'kilometer', 'meter'), or None\n if no reference quantities are set\n\nExamples\n--------\n>>> print(swarm.units) # 'kilometer' if length_scale was set in km\n>>> coords = swarm.coords # Coordinates in kilometers", + "harvested_comments": [ + "'kilometer' if length_scale was set in km", + "Coordinates in kilometers", + "Coordinates have length dimensions", + "Check if model has reference quantities", + "Get length scale from model" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "clip_to_mesh", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 3410, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Whether particles are clipped to remain within mesh boundaries.\n\nWhen True (default), particles that move outside the mesh domain\nduring advection or coordinate updates are removed or repositioned\nto stay within bounds. When False, particles can exist outside the\nmesh domain.\n\nReturns\n-------\nbool\n Current clipping state.\n\nSee Also\n--------\ndont_clip_to_mesh : Context manager to temporarily disable clipping.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "clip_to_mesh", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3431, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set whether particles should be clipped to mesh boundaries.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "dont_clip_to_mesh", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3435, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Context manager that temporarily disables mesh clipping for the swarm.\n`swarm.migrate` is called automatically when exiting the context.\n\nUsage:\n with swarm.dont_clip_to_mesh():\n # swarm operations that should not be clipped to mesh\n swarm.data = new_positions", + "harvested_comments": [ + "swarm operations that should not be clipped to mesh" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "migration_disabled", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3463, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Legacy context manager that completely disables migration.\nUse migration_control(disable=True) for new code.\n\nContext manager that temporarily disables particle migration for the swarm.\nMigration is NOT called when exiting the context. Writes made inside\nthe context are packed to PETSc at exit (only the migration is\nsuppressed \u2014 data is never discarded).\n\nUsage:\n with swarm.migration_disabled():\n # swarm operations that should not trigger migration\n swarm.data = new_positions\n # ... other operations ...\n # migrate() will be skipped during these operations", + "harvested_comments": [ + "swarm operations that should not trigger migration", + "... other operations ...", + "migrate() will be skipped during these operations" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "migration_control", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3483, + "signature": "(self, disable = False)", + "parameters": [ + { + "name": "disable", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Context manager to control particle migration behavior.\n\nParameters\n----------\ndisable : bool\n If False (default), migration is deferred until context exit.\n If True, migration is completely disabled.\n\nExamples\n--------\nDefer migration until end (default)::\n\n with swarm.migration_control():\n swarm.points[mask1] += delta1\n swarm.points[mask2] *= scale\n # Migration happens HERE on exit\n\nCompletely disable migration::\n\n with swarm.migration_control(disable=True):\n # Operations where migration should never happen\n # No migration on exit\n\nIn both modes, variable/coordinate writes made inside the context are\nflushed to the underlying DMSwarm at exit \u2014 only the migration itself\nis deferred (default) or skipped (``disable=True``).", + "harvested_comments": [ + "Migration happens HERE on exit", + "Operations where migration should never happen", + "No migration on exit", + "Flush writes deferred while the flag was set \u2014 suppressing", + "migration must not discard data (SWARM-04). Skipped only" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "populate", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3549, + "signature": "(self, fill_param: Optional[int] = 1)", + "parameters": [ + { + "name": "fill_param", + "type_hint": "Optional[int]", + "default": "1", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Populate the swarm with particles throughout the domain.\n\nParameters\n----------\nfill_param:\n Parameter determining the particle count per cell (per dimension)\n for the given layout, using the mesh degree.\n\nRaises\n------\nRuntimeError\n If the swarm has already been initialized with particles.", + "harvested_comments": [ + "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero", + "It allocates N-1 instead of N, so we add +1 to compensate", + "PETSc 3.24+ fixed this bug, so we use the exact count", + "Invalidate cached data \u2014 the swarm was just given its particles.", + "Any canonical `.data` array created before populate() (legitimate:" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "migrate", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3615, + "signature": "(self, remove_sent_points = True, delete_lost_points = None, max_its = 10)", + "parameters": [ + { + "name": "remove_sent_points", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "delete_lost_points", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "max_its", + "type_hint": null, + "default": "10", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Migrate swarm across processes after coordinates have been updated.\n\nThe algorithm uses a global kD-tree for the centroids of the domains to decide the particle mpi.rank (send to the closest)\nIf the particles are mis-assigned to a particular mpi.rank, the next choice is the second-closest and so on.\n\nA few particles are still not found after this distribution process which probably means they are just outside the mesh.\nIf some points remain lost, they will be deleted if `delete_lost_points` is set.\n\nImplementation note:\n We retained (above) the name `DMSwarmPIC_coor` for the particle field to allow this routine to be inherited by a PIC swarm\n which has this field pre-defined. (We'd need to add a cellid field as well, and re-compute it upon landing)\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.", + "harvested_comments": [ + "Deferred writes must reach the DMSwarm before we read coordinates", + "from it below (no-op unless a migration-suppressed context left", + "pending packs behind, SWARM-04).", + "Informational: migration may move or drop particles. Bump", + "unconditionally; restore is not gated on this counter so a" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "add_particles_with_coordinates", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3811, + "signature": "(self, coordinatesArray) -> int", + "parameters": [ + { + "name": "coordinatesArray", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "int", + "existing_docstring": "Add particles at given coordinates, keeping only locally-owned points.\n\nEach rank filters the input array and adds only the points that fall\nwithin its local domain partition. Non-local points are silently\nignored, so it is safe to pass the same global coordinate array to\nevery rank \u2014 no duplicates will be created.\n\nThis is the recommended method for user code. For pre-partitioned\ndata where each rank already holds only its own points, this method\nalso works correctly.\n\nParameters\n----------\ncoordinatesArray : numpy.ndarray\n Coordinates of new particles, shape ``(n, dim)``.\n\nReturns\n--------\nnpoints : int\n Number of points actually added on this rank.", + "harvested_comments": [ + "-1 means no particles have been added yet (PETSc interface change)", + "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero", + "It allocates N-1 instead of N, so we add +1 to compensate", + "PETSc 3.24+ fixed this bug, so we use the exact count", + "Invalidate cached data \u2014 particle count changed after addNPoints + migrate" + ], + "status": "complete", + "needs": [], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "add_particles_with_global_coordinates", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3885, + "signature": "(self, globalCoordinatesArray, migrate = True, delete_lost_points = True) -> int", + "parameters": [ + { + "name": "globalCoordinatesArray", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "migrate", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "delete_lost_points", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": "int", + "existing_docstring": "Insert the full coordinate array on every rank (low-level primitive).\n\nEvery rank inserts **all** supplied points into its local swarm.\nThere is no locality filtering and no deduplication: migration is a\nscatter that routes each inserted particle to the rank owning its\nlocation, so passing the same array on every rank at ``np`` processes\nyields ``np`` copies of every point.\n\nCorrect usage is one of:\n\n- ``migrate=False``, where each rank deliberately keeps a full copy\n of the points (the global-evaluation / mesh-transfer pattern), or\n- input that is pre-partitioned across ranks \u2014 or supplied on rank 0\n only, with empty ``(0, dim)`` arrays elsewhere \u2014 followed by\n migration to route each point to its owner.\n\nFor general use, prefer :meth:`add_particles_with_coordinates`,\nwhich accepts a rank-identical array and filters non-local points so\neach point is added exactly once.\n\nParameters\n----------\nglobalCoordinatesArray : numpy.ndarray\n Coordinates of new particles, shape ``(n, dim)``.\nmigrate : bool\n Run PETSc swarm migration after insertion (default True).\ndelete_lost_points : bool\n Remove particles that fall outside any rank's domain\n during migration (default True).\n\nReturns\n--------\nnpoints : int\n Number of points added on this rank before migration.", + "harvested_comments": [ + "-1 means no particles have been added yet", + "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero", + "It allocates N-1 instead of N, so we add +1 to compensate", + "PETSc 3.24+ fixed this bug, so we use the exact count", + "Informational: population changed even if migrate=False is" + ], + "status": "complete", + "needs": [], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "save", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 3982, + "signature": "(self, filename: int, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential = False)", + "parameters": [ + { + "name": "filename", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "compression", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "compressionType", + "type_hint": "Optional[str]", + "default": "'gzip'", + "description": "" + }, + { + "name": "force_sequential", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Save the swarm coordinates to a h5 file.\n\nParameters\n----------\nfilename :\n The filename of the swarm checkpoint file to save to disk.\ncompression :\n Add compression to the h5 files (saves space but increases write times with increasing no. of processors)\ncompressionType :\n Type of compression to use, 'gzip' and 'lzf' supported. 'gzip' is default. Compression also needs to be set to 'True'.", + "harvested_comments": [ + "BUGFIX(#151): the previous parallel path called", + "h5f.create_dataset(\"coordinates\", data=points_data_copy)", + "collectively, but each rank passed its own local-sized array.", + "In parallel HDF5 every rank must specify the *same* dataset", + "shape on a collective create_dataset; passing different shapes" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "add_variable", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4176, + "signature": "(self, name, size = 1, dtype = float, proxy_degree = 2, _nn_proxy = False, units = None)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "size", + "type_hint": null, + "default": "1", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "float", + "description": "" + }, + { + "name": "proxy_degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "_nn_proxy", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add a variable to the swarm.\n\nVariables must be created before the swarm is populated with particles.\nOnce swarm.populate() or similar methods are called, PETSc finalizes\nfield registration and no new variables can be added.\n\nParameters\n----------\nname : str\n Variable name\nsize : int, default 1\n Number of components (1 for scalar, 2-3 for vector, etc.)\ndtype : type, default float\n Data type (float or int)\nproxy_degree : int, default 2\n Degree for mesh proxy variable interpolation\n_nn_proxy : bool, default False\n Internal parameter for nearest-neighbor proxy\nunits : str, optional\n Physical units for this variable (e.g., \"kg/m^3\", \"m/s\")\n\nReturns\n-------\nSwarmVariable\n The created swarm variable\n\nRaises\n------\nRuntimeError\n If swarm is already populated with particles\n\nExamples\n--------\nCorrect usage:\n>>> swarm = uw.swarm.Swarm(mesh)\n>>> material = swarm.add_variable(\"material\", 1, dtype=int)\n>>> temperature = swarm.add_variable(\"temperature\", 1)\n>>> swarm.populate(fill_param=3) # Populate after creating variables\n\nIncorrect usage (will raise error):\n>>> swarm = uw.swarm.Swarm(mesh)\n>>> swarm.populate(fill_param=3)\n>>> material = swarm.add_variable(\"material\", 1) # ERROR!", + "harvested_comments": [ + "Populate after creating variables", + "Check early to provide a clear error message", + "Create variables first\\n\"", + "Then populate with particles\"" + ], + "status": "complete", + "needs": [], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "petsc_save_checkpoint", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4253, + "signature": "(self, swarmName: str, index: int, outputPath: Optional[str] = '')", + "parameters": [ + { + "name": "swarmName", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "index", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "outputPath", + "type_hint": "Optional[str]", + "default": "''", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Use PETSc to save the swarm and attached data to a .pbin and xdmf file.\n\nParameters\n----------\nswarmName :\n Name of the swarm to save.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "write_timestep", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4277, + "signature": "(self, filename: str, swarmname: str, index: int, swarmVars: Optional[list] = None, outputPath: Optional[str] = '', time: Optional[int] = None, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential: Optional[bool] = False)", + "parameters": [ + { + "name": "filename", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "swarmname", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "index", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "swarmVars", + "type_hint": "Optional[list]", + "default": "None", + "description": "" + }, + { + "name": "outputPath", + "type_hint": "Optional[str]", + "default": "''", + "description": "" + }, + { + "name": "time", + "type_hint": "Optional[int]", + "default": "None", + "description": "" + }, + { + "name": "compression", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "compressionType", + "type_hint": "Optional[str]", + "default": "'gzip'", + "description": "" + }, + { + "name": "force_sequential", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Save data to h5 and a corresponding xdmf for visualisation using h5py.\n\nParameters\n----------\nswarmName :\n Name of the swarm to save.\nswarmVars :\n List of swarm objects to save.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.\ntime :\n Attach the time to the generated xdmf.\ncompression :\n Whether to compress the h5 files [bool].\ncompressionType :\n The type of compression to use. 'gzip' and 'lzf' are the supported types, with 'gzip' as the default.", + "harvested_comments": [ + "This will eliminate the issue of whether or not to put path separators in the", + "outputPath. Also does the right thing if outputPath is \"\"", + "check the directory where we will write checkpoint", + "get directory", + "check if path exists" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "vars", + "kind": "property", + "file": "src/underworld3/swarm.py", + "line": 4427, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "List of SwarmVariables attached to this swarm.\n\nReturns\n-------\nlist\n List of :class:`SwarmVariable` objects defined on this swarm.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "snapshot_payload", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4450, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Return a self-contained dict describing this swarm's state.\n\nCaptured: per-rank particle coordinates (from\n``DMSwarmPIC_coor``) and every user swarm-variable's data\narray. PETSc-internal variables (``DMSwarmPIC_coor``,\n``DMSwarm_X0``) are excluded \u2014 their\ncontents either come from the captured coords or are\nregenerated on the next solve.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "apply_snapshot_payload", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4480, + "signature": "(self, payload: dict) -> None", + "parameters": [ + { + "name": "payload", + "type_hint": "dict", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Rebuild this swarm's local particle population from a payload.\n\nAlgorithm:\n\n1. Drop every current local particle (``dm.removePoint`` from\n the end is O(1) per call, O(N) total).\n2. Add the captured-rank's particles back via the raw PETSc\n primitives \u2014 ``addNPoints`` then writing the coord field\n directly. We deliberately bypass\n :meth:`add_particles_with_coordinates` because that method\n filters via ``points_in_domain`` (slow) and triggers\n ``dm.migrate`` (unnecessary \u2014 saved coords were already\n local at capture time, and the mesh hasn't changed).\n3. Write captured per-variable data back. The local particle\n count matches the captured count because we just put the\n same particles back in the same order.\n\nThis bumps ``_population_generation`` once (from the addNPoints\nstep in restore), which is correct: the population *did* just\nchange. Downstream consumers that care can compare against the\ncaptured value in ``payload['captured_population_generation']``.", + "harvested_comments": [ + "Step 1: clear local population. removePoint() removes the last", + "particle, so this is O(N) total.", + "Step 2: re-add. add raw points, write coords + ranks directly.", + "Invalidate canonical-data caches \u2014 the underlying arrays", + "have been reallocated by the addNPoints path." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "access", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4738, + "signature": "(self, *writeable_vars)", + "parameters": [ + { + "name": "*writeable_vars", + "type_hint": "SwarmVariable", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Dummy access manager that provides deferred sync for backward compatibility.\nUses NDArray_With_Callback.delay_callbacks_global() internally.\n\nThis is a compatibility wrapper that allows existing code using the access()\ncontext manager to work with the new direct-access variable interfaces.\nAll variable modifications are deferred and synchronized at context exit.\n\nParameters\n----------\nwriteable_vars\n Variables that will be modified (ignored - all variables are writable\n with the new interface, this parameter is kept for API compatibility)\n\nReturns\n-------\nContext manager that defers variable synchronization until exit\n\nNotes\n-----\nThis method is deprecated. New code should access variable.data or\nvariable.array directly without requiring an access context.", + "harvested_comments": [ + "Use NDArray_With_Callback global delay context for deferred sync", + "This triggers all accumulated callbacks from all variables" + ], + "status": "complete", + "needs": [], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "estimate_dt", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 5015, + "signature": "(self, V_fn)", + "parameters": [ + { + "name": "V_fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Calculates an appropriate advective timestep for the given\nmesh and velocity configuration.", + "harvested_comments": [ + "we'll want to do this on an element by element basis", + "for more general mesh", + "first let's extract a max global velocity magnitude", + "If vel is unit-aware (UnitAwareArray), nondimensionalise it to get", + "consistent nondimensional values that match mesh._radii" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": true + }, + { + "name": "state", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 602, + "signature": "(self) -> 'DDtSymbolicState'", + "parameters": [], + "returns": "'DDtSymbolicState'", + "existing_docstring": "Return a snapshot-of-state dataclass for this DDt instance.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "state", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 613, + "signature": "(self, s: 'DDtSymbolicState') -> None", + "parameters": [ + { + "name": "s", + "type_hint": "'DDtSymbolicState'", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Write a captured state back. Reconciles derived coefficients.", + "harvested_comments": [ + "Re-derive BDF/AM coefficients so downstream reads see values", + "consistent with the restored primary state without waiting", + "for the next update_pre_solve." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "psi_fn", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 645, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current symbolic expression :math:`\\psi` being tracked.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "psi_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 650, + "signature": "(self, new_fn)", + "parameters": [ + { + "name": "new_fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the tracked symbolic expression.", + "harvested_comments": [ + "Optionally, one could check for matching shape; here we update both." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "effective_order", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 672, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current effective BDF order, accounting for history startup.\n\nFor BDF order k, k distinct history values are needed. During\nstartup, ``effective_order`` ramps from 1 to ``self.order`` as\nsuccessive solves populate the history slots with distinct values.", + "harvested_comments": [ + "BDF-k requires k completed solves to have k distinct history values.", + "With 0 or 1 completed solves \u2192 order 1. Order 2 needs \u22652 solves." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "update_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 683, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Copy current :math:`\\psi` to the first history slot ``psi_star[0]``.", + "harvested_comments": [ + "Update the first history element with a copy of the current \u03c8." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "initialise_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 688, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.\n\nCalled automatically on the first ``update_pre_solve``. Can also\nbe called manually after setting initial conditions.", + "harvested_comments": [ + "Propagate the initial history to all history steps." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "initiate_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 701, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: use ``initialise_history`` instead.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "update", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 705, + "signature": "(self, dt, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update history (alias for ``update_pre_solve``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "update_pre_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 715, + "signature": "(self, dt, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pre-solve update hook. Auto-initialises history on first call.", + "harvested_comments": [ + "Update coefficient values for current effective_order and dt" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "update_post_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 733, + "signature": "(self, dt, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Shift history chain after solve: :math:`\\psi^{*n} \\leftarrow \\psi^{*(n-1)}`.", + "harvested_comments": [ + "Record timestep history for variable-dt BDF", + "Shift history: copy each element down the chain." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "bdf_coefficients", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 761, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current BDF coefficients [c0, c1, ...] accounting for variable timesteps.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "bdf", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 765, + "signature": "(self, order: Optional[int] = None)", + "parameters": [ + { + "name": "order", + "type_hint": "Optional[int]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Backward differentiation approximation of the time-derivative of \u03c8.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. The coefficient values are updated each step in\n``update_pre_solve`` \u2014 no JIT recompilation needed when the\norder ramps up or the timestep changes.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility). The effective order is\n controlled by the coefficient values.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "adams_moulton_flux", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 781, + "signature": "(self, order: Optional[int] = None)", + "parameters": [ + { + "name": "order", + "type_hint": "Optional[int]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "update_exp_coefficients", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 794, + "signature": "(self, dt, tau_eff)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tau_eff", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update the ETD-2 (exponential) coefficient values for this step.\n\nSets ``self._exp_coeffs[0].sym = \u03b1`` and ``self._exp_coeffs[1].sym = \u03c6``\nfrom current ``dt`` and ``tau_eff`` (Maxwell relaxation time\n:math:`\\tau = \\eta_\\mathrm{eff}/\\mu`). Called by the constitutive\nmodel (which owns \u03c4_eff) before each solve, peer to the BDF/AM\ncoefficient updates that happen automatically in\n``update_pre_solve``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Symbolic", + "is_public": true + }, + { + "name": "state", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 968, + "signature": "(self) -> 'DDtEulerianState'", + "parameters": [], + "returns": "'DDtEulerianState'", + "existing_docstring": "Return a snapshot-of-state dataclass for this Eulerian DDt.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "psi_fn", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 1007, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current symbolic expression :math:`\\psi` being tracked.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "psi_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1012, + "signature": "(self, new_fn)", + "parameters": [ + { + "name": "new_fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the tracked expression.", + "harvested_comments": [ + "self._psi_star_projection_solver.uw_function = self.psi_fn" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "effective_order", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 1094, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current effective BDF order, accounting for history startup.\n\nFor BDF order k, k distinct history values are needed. During\nstartup, ``effective_order`` ramps from 1 to ``self.order`` as\nsuccessive solves populate the history slots with distinct values.", + "harvested_comments": [ + "BDF-k requires k completed solves to have k distinct history values.", + "With 0 or 1 completed solves \u2192 order 1. Order 2 needs \u22652 solves." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "update_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1105, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Copy current :math:`\\psi` to ``psi_star[0]`` via evaluation or projection.", + "harvested_comments": [ + "## update first value in history chain", + "## avoids projecting if function can be evaluated" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "initialise_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1123, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.\n\nCalled automatically on the first ``update_pre_solve``. Can also\nbe called manually after setting initial conditions to ensure\nthe history chain starts from the correct state.", + "harvested_comments": [ + "## set up all history terms to the initial values" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "set_initial_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1139, + "signature": "(self, values, dt = None)", + "parameters": [ + { + "name": "values", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dt", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Plant history values for BDF restart or analytical IC.\n\nBypasses the automatic ``effective_order`` ramp so the very\nfirst solve runs at the full BDF order rather than starting at\nBDF-1. Use this when you have known values at :math:`t` and\npast times \u2014 e.g. an analytical periodic solution, or a\ncheckpointed history loaded from disk.\n\nParameters\n----------\nvalues : sequence of length ``self.order``\n ``values[k]`` is :math:`\\psi` at :math:`t - k\\,\\Delta t`,\n i.e. ``values[0]`` is the current state. Each entry must\n be assignable to ``psi_star[k].array`` \u2014 either an array\n of matching shape, or a scalar that broadcasts.\ndt : float, optional\n Uniform timestep assumed between history slots. Required\n for ``order >= 2`` to seed correct multistep coefficients\n on the first solve. Ignored for ``order = 1``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "initiate_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1182, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: use ``initialise_history`` instead.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "update", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1186, + "signature": "(self, dt, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update history (alias for ``update_pre_solve``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "update_pre_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1196, + "signature": "(self, dt, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pre-solve: auto-initialise history and apply advection correction.\n\nOn the first call, automatically initialises history from the\ncurrent field values. If V_fn is set, also applies an explicit\ngrid-based advection correction so that bdf() approximates the\nmaterial derivative D\u03c6/Dt rather than \u2202\u03c6/\u2202t.", + "harvested_comments": [ + "Update coefficient values for current effective_order and dt", + "Build u\u00b7\u2207\u03c6 symbolically for each component of psi_fn", + "psi_fn is a Matrix; V_fn is also a Matrix. For scalar", + "psi_fn the shape is (1,1); for vector it is (1,dim).", + "number of tracked components" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "update_post_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1244, + "signature": "(self, dt, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Shift history chain after solve: :math:`\\psi^{*n} \\leftarrow \\psi^{*(n-1)}`.", + "harvested_comments": [ + "Record timestep history for variable-dt BDF", + "## copy values down the chain", + "## update the history fn" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "bdf_coefficients", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 1274, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current BDF coefficients [c0, c1, ...] accounting for variable timesteps.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "bdf", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1278, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Backward differentiation approximation of the time-derivative of :math:`\\psi`.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "adams_moulton_flux", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1291, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "update_exp_coefficients", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1304, + "signature": "(self, dt, tau_eff)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tau_eff", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update the ETD-2 (exponential) coefficient values for this step.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": true + }, + { + "name": "on_remesh", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1804, + "signature": "(self, ctx)", + "parameters": [ + { + "name": "ctx", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adapt-time hook: ALE for the SL history stack.\n\nTwo branches:\n\n* **Standard ALE (smooth adapt).** The SL-owned vars\n (``psi_star[i]``, ``forcing_star``, ``_workVar``, the\n flattening view ``_psi_star_flat_var``) are CARRY +\n operator-managed \u2014 the generic per-variable pass already\n skipped them, and we leave their ``.data`` untouched here.\n Accumulate ``ctx.total_disp`` onto\n ``self._pending_v_mesh_disp`` so the next\n :meth:`update_pre_solve` runs the SL trace-back along\n ``(V_fn \u2212 v_mesh)`` with ``v_mesh = \u0394x / dt`` \u2014 that\n subtraction is exactly what compensates for the arbitrary\n mesh motion when reading the CARRY'd history. One-step\n pulse: the next solve consumes \u0394x and clears it.\n\n* **Opt-out (e.g. OT_adapt reset).** When the adapt is a\n discrete jump rather than a smooth displacement\n (``ctx.scratch.get(\"ale_opt_out\")``), the linear\n ``\u0394x/dt \u2192 v_mesh`` interpretation breaks down. Fall back to\n Phase-1 REMAP for this DDt's managed vars: call\n :func:`~underworld3.discretisation.remesh.remap_var_set` with\n the pre-move snapshot in ``ctx.managed_snapshot``. The\n pending ``v_mesh`` is cleared because REMAP already brought\n the history onto the new positions.\n\nAccumulation across multiple adapts before one solve is\nlinear: ``v_mesh_disp += ctx.total_disp``. The trace-back uses\nthe SUM divided by the next ``dt``, which is the correct\nnode-frame velocity for that step.", + "harvested_comments": [ + "Which DDt-owned vars do I own? Collect from the mesh.vars", + "registry by managed-by identity (matches the stamping in", + "REMAP fallback. ctx.managed_snapshot holds my vars'", + "pre-move .data (the helper snapshots all managed vars).", + "remap_var_set deforms back, restores, evaluates at new" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "psi_fn", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 1933, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current symbolic expression :math:`\\psi` being tracked.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "psi_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1938, + "signature": "(self, new_fn)", + "parameters": [ + { + "name": "new_fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the tracked expression and propagate to the projection's source.\n\nWhen :meth:`enable_source_snapshot` has been called, ``psi_star[0]``\nsymbols in ``new_fn`` are transparently substituted with the\nsnapshot variable's symbols before the source is pushed to the\nprojection solver \u2014 so the projection becomes a true one-shot\nGalerkin projection regardless of whether ``new_fn`` references\n``psi_star[0]``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "enable_source_snapshot", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1978, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Enable snapshot substitution in the projection's source field.\n\nCall this once when the source expression (``psi_fn``) references\n``psi_star[0]`` itself \u2014 without it the projection's residual\n``(target \u2212 flux(psi_star[0]))\u00b7weight`` is implicit in the target\nbecause target and source share the same data field. With Min-mode\nplasticity at the yield kink, the implicit projection admits two\nfixed points (elastic and yield branches); under timestep change the\niteration drifts to the elastic-branch fixed point and \u03c3 violates\nthe yield surface.\n\nThe snapshot is a separate mesh variable matching ``psi_star[0]``'s\nshape/vtype/degree. Each call to ``update_pre_solve`` copies\n``psi_star[0].array \u2192 psi_snapshot.array``, freezing the source's\ninput for the upcoming projection. Substitution makes the\nprojection's compiled C code read from ``psi_snapshot.array``\ninstead of ``psi_star[0].array`` \u2014 there's no recompile per step,\njust a memcpy.\n\nIdempotent: safe to call more than once.", + "harvested_comments": [ + "Currently only wired for tensor projections (the case that", + "exposed the bug). Scalar/vector extension is straightforward", + "if needed later.", + "NOTE: this currently registers a persistent MeshVariable in the", + "mesh DM, which is overkill for a transient buffer that's only" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "effective_order", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 2043, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current effective BDF order, accounting for history startup.\n\nFor BDF order k, k distinct history values are needed. During\nstartup, ``effective_order`` ramps from 1 to ``self.order`` as\nsuccessive solves populate the history slots with distinct values.", + "harvested_comments": [ + "BDF-k requires k completed solves to have k distinct history values.", + "With 0 or 1 completed solves \u2192 order 1. Order 2 needs \u22652 solves." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "initialise_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2054, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.\n\nCalled automatically on the first ``update_pre_solve``. Can also\nbe called manually after setting initial conditions.", + "harvested_comments": [ + "Evaluate psi_fn at psi_star node positions and store in psi_star[0]", + "Fallback: project psi_fn onto psi_star[0] via the SNES projector.", + "Route through the shared builder so snapshot substitution", + "semantics are consistent.", + "Fan out flat result to tensor psi_star[0]" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "set_initial_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2101, + "signature": "(self, values, dt = None)", + "parameters": [ + { + "name": "values", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dt", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Plant history values for BDF restart or analytical IC.\n\nBypasses the automatic ``effective_order`` ramp so the very\nfirst solve runs at the full BDF order rather than starting\nat BDF-1. Use this when you have known values at :math:`t`\nand past times \u2014 e.g. an analytical periodic solution, or a\ncheckpointed history loaded from disk.\n\nParameters\n----------\nvalues : sequence of length ``self.order``\n ``values[k]`` is :math:`\\psi` at :math:`t - k\\,\\Delta t`,\n i.e. ``values[0]`` is the current state. Each entry must\n be assignable to ``psi_star[k].array`` \u2014 either an array\n of matching shape, or a scalar that broadcasts.\ndt : float, optional\n Uniform timestep assumed between history slots. Required\n for ``order >= 2`` to seed correct multistep coefficients\n on the first solve. Ignored for ``order = 1``.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "initiate_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2144, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: use ``initialise_history`` instead.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "update", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2249, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False, dt_physical: Optional = None)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "dt_physical", + "type_hint": "Optional", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update history (alias for ``update_pre_solve``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "update_post_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2260, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False, dt_physical: Optional[float] = None)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "dt_physical", + "type_hint": "Optional[float]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Post-solve: record timestep and increment solve counter.", + "harvested_comments": [ + "Record timestep history for variable-dt BDF" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "update_pre_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2280, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False, dt_physical: Optional[float] = None, store_result: Optional[bool] = True, monotone_mode: Optional[str] = '__instance__')", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "dt_physical", + "type_hint": "Optional[float]", + "default": "None", + "description": "" + }, + { + "name": "store_result", + "type_hint": "Optional[bool]", + "default": "True", + "description": "" + }, + { + "name": "monotone_mode", + "type_hint": "Optional[str]", + "default": "'__instance__'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Sample upstream values along characteristics before solve.\n\nOn the first call, automatically initialises history from the\ncurrent field values so that bdf() returns zero on the first step.\n\nParameters\n----------\nstore_result : bool, optional\n If True (default), evaluate psi_fn at current positions and store\n in psi_star[0] before advection \u2014 the standard DDt behaviour.\n If False, skip this step and the history shift: only advect the\n existing psi_star levels upstream. Used by VE_Stokes where\n psi_star[0] already contains the projected actual stress from\n the previous solve.\nmonotone_mode : str or None, optional\n Override the instance ``self.monotone_mode`` for this call.\n Default ``\"__instance__\"`` (sentinel) means \"use whatever is\n on the instance\". Pass ``None``, ``\"clamp\"``, or ``\"pick\"``\n to force a particular mode for one call.", + "harvested_comments": [ + "Resolve monotone_mode: explicit kwarg overrides instance attr.", + "Old-frame reach-back (mutually exclusive with the ALE pulse:", + "``on_remesh`` stashes ``_oldframe_X`` INSTEAD of a v_mesh disp,", + "so ``_ale_active`` is False below whenever this is True). When", + "active the foot is computed from the physical V (no v_mesh) and" + ], + "status": "complete", + "needs": [], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "bdf_coefficients", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 2765, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current BDF coefficients [c0, c1, ...] accounting for variable timesteps.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "bdf", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2769, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Backward differentiation approximation of the time-derivative of :math:`\\psi`.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "adams_moulton_flux", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2782, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "update_exp_coefficients", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2795, + "signature": "(self, dt, tau_eff)", + "parameters": [ + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tau_eff", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update the scalar ETD-2 (exponential) coefficient UWexpressions.\n\nSets ``self._exp_coeffs[0].sym = \u03b1 = exp(-\u0394t/\u03c4_eff)`` and\n``self._exp_coeffs[1].sym = \u03c6 = (1-\u03b1)/(\u0394t/\u03c4_eff)`` so the next solve\nuses the correct exponential coefficients via PetscDSSetConstants\non the next ``_update_constants`` call.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "update_forcing_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2815, + "signature": "(self, forcing_fn = None, evalf = False, verbose = False)", + "parameters": [ + { + "name": "forcing_fn", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "evalf", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Refresh ``forcing_star`` from ``forcing_fn`` via direct nodal evaluation.\n\nUsed by ETD-2 exponential integration to store the current\nstrain-rate field as :math:`\\dot\\varepsilon^{n}` for the next\nstep's history term. Called by the constitutive model from the\nsolver's post-solve hook. Direct nodal evaluation (rather than an\nL2 projection through SNES) is sufficient because the strain rate\nis :math:`\\nabla\\mathbf{u}`, well-defined at nodes, with no\nhistory-coupled term that would make a projection implicit.\n\nUnit handling\n-------------\n``forcing_star`` is allocated with ``units=None`` (see\n``__init__``). When the model is unit-aware, ``forcing_fn`` is a\nsymbolic expression of the velocity field whose evaluation\nreturns a ``UnitAwareArray`` carrying strain-rate units (1/time).\nWe non-dimensionalise that result via the active scaling system\nbefore assigning to ``forcing_star.array``, which keeps the\nstored values consistent with the rest of the variable storage\n(codebase convention: variable storage is non-dimensional;\nunits are re-attached at the ``.data`` interface). When the\nmodel is not unit-aware, the evaluation returns a plain ndarray\nand assignment is a straight numpy copy.\n\nParameters\n----------\nforcing_fn : sympy expression, optional\n Symbolic strain-rate field to evaluate at each node. If\n None, falls back to ``self._forcing_fn`` (set by the\n constitutive model at solve-attach time). No-op if neither\n is set or ``with_forcing_history=False``.\nevalf : bool, optional\n Forwarded to ``uw.function.evaluate`` (forces numerical\n evaluation when True).\nverbose : bool, optional\n Enable verbose output.", + "harvested_comments": [ + "constitutive model hasn't wired the forcing source yet", + "Use non-dimensional coords for evaluate() (mirrors the psi_star", + "path in update_pre_solve)", + "If the evaluation returned units (model is unit-aware),", + "non-dimensionalise before storing \u2014 keeps forcing_star's" + ], + "status": "complete", + "needs": [], + "parent_class": "SemiLagrangian", + "is_public": true + }, + { + "name": "on_remesh", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3072, + "signature": "(self, ctx)", + "parameters": [ + { + "name": "ctx", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adapt-time hook (Phase 1 no-op).\n\nLagrangian history is carried by the underlying swarm (each\nparticle holds its own ``psi_star`` values), so there is no\nmesh-side state to transfer on an adapt \u2014 the swarm's particle\npositions stay put and the new cells re-claim them. Method is\ndefined so the registration shim has a target.", + "harvested_comments": [ + "Phase 1: explicitly unused" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "effective_order", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 3134, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current effective BDF order, accounting for history startup.", + "harvested_comments": [ + "BDF-k requires k completed solves to have k distinct history values.", + "With 0 or 1 completed solves \u2192 order 1. Order 2 needs \u22652 solves." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "initialise_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3140, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.\n\nCalled automatically on the first ``update_pre_solve``. Can also\nbe called manually after setting initial conditions.", + "harvested_comments": [ + "Component-wise write through the canonical (N, components) storage.", + "Indexing the SwarmVariable itself (``psi_star_0[i, j]``) returns a", + "*symbolic* component with no ``.data`` \u2014 the modern component", + "address is ``.data[:, var._data_layout(i, j)]`` (audit SWARM-06).", + "Copy to all other history slots" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "initiate_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3169, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: use ``initialise_history`` instead.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "update", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3180, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update history (alias for ``update_post_solve``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "update_pre_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3190, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pre-solve: auto-initialise history on first call.", + "harvested_comments": [ + "Update coefficient values for current effective_order and dt" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "update_post_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3208, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Shift history chain and advect swarm after solve.", + "harvested_comments": [ + "Record timestep history for variable-dt BDF", + "copy the information down the chain", + "Now update the swarm variable", + "Grab the current psi values at the (pre-advection) particle", + "positions via the canonical component storage (audit SWARM-06)." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "bdf_coefficients", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 3261, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current BDF coefficients [c0, c1, ...] accounting for variable timesteps.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "bdf", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3265, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Backward differentiation approximation of the time-derivative of :math:`\\psi`.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "adams_moulton_flux", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3278, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Lagrangian", + "is_public": true + }, + { + "name": "effective_order", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 3491, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current effective BDF order, accounting for history startup.", + "harvested_comments": [ + "BDF-k requires k completed solves to have k distinct history values.", + "With 0 or 1 completed solves \u2192 order 1. Order 2 needs \u22652 solves." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "initialise_history", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3497, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.\n\nCalled automatically on the first ``update_pre_solve``. Can also\nbe called manually after setting initial conditions.", + "harvested_comments": [ + "Component-wise write through the canonical (N, components) storage.", + "Indexing the SwarmVariable itself (``psi_star_0[i, j]``) returns a", + "*symbolic* component with no ``.data`` \u2014 the modern component", + "address is ``.data[:, var._data_layout(i, j)]`` (audit SWARM-06).", + "Copy to all other history slots" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "initiate_history_fn", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3526, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Deprecated: use ``initialise_history`` instead.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "update", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3537, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update history (alias for ``update_post_solve``).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "update_pre_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3547, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pre-solve: auto-initialise history on first call.", + "harvested_comments": [ + "Update coefficient values for current effective_order and dt" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "update_post_solve", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3565, + "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "parameters": [ + { + "name": "dt", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Shift history chain and evaluate current :math:`\\psi` on swarm.", + "harvested_comments": [ + "Record timestep history for variable-dt BDF", + "copy the information down the chain", + "Blend the freshly-evaluated psi into slot 0 component-by-component", + "through the canonical (N, components) storage (audit SWARM-06)." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "bdf_coefficients", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 3619, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Current BDF coefficients [c0, c1, ...] accounting for variable timesteps.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "bdf", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3623, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Backward differentiation approximation of the time-derivative of :math:`\\psi`.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "adams_moulton_flux", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3636, + "signature": "(self, order = None)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nReturns a fixed-structure symbolic expression using UWexpression\ncoefficients. Values are updated each step in ``update_pre_solve``.\n\nParameters\n----------\norder : int, optional\n Ignored (kept for API compatibility).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Lagrangian_Swarm", + "is_public": true + }, + { + "name": "simple_mesh", + "kind": "function", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 18, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Create a simple 2D mesh for testing.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "test_basic_creation", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 31, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test basic metric creation from h-field.", + "harvested_comments": [ + "Check metric = 1/h\u00b2" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestCreateMetric", + "is_public": true + }, + { + "name": "test_variable_h_field", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 44, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test metric with spatially varying h-field.", + "harvested_comments": [ + "h varies linearly from 0.02 to 0.1 across domain" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestCreateMetric", + "is_public": true + }, + { + "name": "test_gaussian_gradient", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 61, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test metric from gradient of Gaussian field.", + "harvested_comments": [ + "h should be in specified range", + "Near center (high gradient) should have smaller h" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestMetricFromGradient", + "is_public": true + }, + { + "name": "test_uniform_field_uses_h_max", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 86, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test that uniform field (zero gradient) uses h_max everywhere.", + "harvested_comments": [ + "Constant field", + "Should be h_max everywhere (no gradient)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestMetricFromGradient", + "is_public": true + }, + { + "name": "test_profiles", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 99, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test different interpolation profiles.", + "harvested_comments": [ + "Linear field - constant gradient" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestMetricFromGradient", + "is_public": true + }, + { + "name": "test_basic_mapping", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 120, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test basic indicator-to-metric mapping.", + "harvested_comments": [ + "High indicator at center", + "High indicator should map to small h" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestMetricFromField", + "is_public": true + }, + { + "name": "test_invert_option", + "kind": "method", + "file": "src/underworld3/tests/test_adaptivity_metrics.py", + "line": 141, + "signature": "(self, simple_mesh)", + "parameters": [ + { + "name": "simple_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Test that invert=True swaps the mapping.", + "harvested_comments": [ + "Linear from 0 to 1", + "Without invert: high indicator (right side) -> small h", + "With invert: high indicator (right side) -> large h", + "Normal: right (high indicator) should have smaller h", + "Inverted: right (high indicator) should have larger h" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TestMetricFromField", + "is_public": true + }, + { + "name": "start", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 74, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Start PETSc performance logging.\n\nCall this at the beginning of your script/notebook to enable comprehensive\nperformance tracking. Works immediately in Jupyter - no environment variables needed!\n\nThis captures:\n- Decorated Python functions\n- All PETSc operations (MatMult, KSPSolve, VecNorm, etc.)\n- Memory usage and allocation\n- Floating point operations (flops)\n- MPI communication (in parallel runs)\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... do work ...\n>>> uw.timing.print_table() # View results\n\nNotes\n-----\n- Safe to call multiple times (subsequent calls are no-ops)\n- Zero overhead when not enabled\n- Can be called anywhere before performance-critical code", + "harvested_comments": [ + "... do work ...", + "View results" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "stop", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 104, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Stop PETSc logging (currently a no-op - PETSc logging runs until view()).\n\nProvided for API compatibility with legacy timing module.\nPETSc logging is lightweight and can run continuously.", + "harvested_comments": [ + "PETSc logging doesn't need explicit stopping" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "reset", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 114, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Reset timing data.\n\nClears all accumulated PETSc logging data and starts fresh.\nUseful for timing specific sections of code.\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... setup code (not timed) ...\n>>> uw.timing.reset()\n>>> # ... performance-critical code (timed) ...\n>>> uw.timing.print_table()", + "harvested_comments": [ + "... setup code (not timed) ...", + "... performance-critical code (timed) ...", + "Note: PETSc doesn't have a true \"reset\" - we'd need to stop and restart", + "For now, this is a no-op but documented for API compatibility" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "print_table", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 138, + "signature": "(filename = None, format = 'auto')", + "parameters": [ + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "format", + "type_hint": null, + "default": "'auto'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Display comprehensive performance results.\n\nShows timing for:\n- Decorated Python functions\n- PETSc operations (solvers, matrix ops, etc.)\n- Memory usage\n- Flop counts\n- MPI communication (parallel runs)\n\nParameters\n----------\nfilename : str, optional\n If provided, write results to file. Extension determines format:\n - ``.csv`` : Spreadsheet-compatible CSV format\n - ``.txt`` or other : Human-readable ASCII table\nformat : str, optional\n Override automatic format detection:\n - ``\"auto\"`` : Detect from filename (default)\n - ``\"ascii\"`` : Human-readable table\n - ``\"csv\"`` : Comma-separated values\n\nExample\n-------\n>>> uw.timing.start()\n>>> # ... do work ...\n>>> uw.timing.print_table() # Print to console\n>>> uw.timing.print_table(\"results.csv\") # Save as CSV\n\nNotes\n-----\n**High-CPU-count usage (\u22731000 ranks): prefer CSV output.**\n\nIssue #134 (gthyagi, 2026-04-23): the underlying PETSc ``PetscLogView``\nASCII output path can hang at extreme rank counts on some clusters\n(BD-integral routines + ASCII table emit appear to be the trigger),\nwhile the CSV write path uses a different, less collective-heavy\nstrategy and avoids the issue. If your job is large enough that\ntiming-output cost matters, write to a ``.csv`` filename:\n\n>>> uw.timing.print_table(\"results.csv\") # safe at any scale\n\nThe behaviour is in PETSc, not Underworld; choosing CSV at scale is\nthe recommended workaround.", + "harvested_comments": [ + "... do work ...", + "Print to console", + "Save as CSV", + "134 (gthyagi, 2026-04-23): the underlying PETSc ``PetscLogView``", + "safe at any scale" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "enable_petsc_logging", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 192, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Enable PETSc performance logging.\n\nCalled automatically by start(). Can also be called directly.\nSafe to call multiple times.", + "harvested_comments": [ + "Already enabled" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "print_petsc_log", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 211, + "signature": "(filename = None, format = 'auto')", + "parameters": [ + { + "name": "filename", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "format", + "type_hint": null, + "default": "'auto'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Display or save PETSc performance logging summary.\n\nParameters\n----------\nfilename : str, optional\n If provided, write log to this file. Otherwise print to console.\n File extension determines format:\n - `.csv` : Comma-separated values (spreadsheet-compatible)\n - `.txt` or other : Human-readable ASCII table (default)\nformat : str, optional\n Override automatic format detection. Options:\n - \"auto\" : Detect from filename extension (default)\n - \"ascii\" : Human-readable table\n - \"csv\" : Comma-separated values\n\nExample\n-------\n>>> uw.timing.start()\n>>> # ... run simulation ...\n>>> uw.timing.print_petsc_log() # Console output\n>>> uw.timing.print_petsc_log(\"timing.csv\") # CSV for analysis", + "harvested_comments": [ + "... run simulation ...", + "Console output", + "CSV for analysis", + "Determine format from extension or explicit parameter", + "Create viewer with appropriate format" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "routine_timer_decorator", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 272, + "signature": "(routine, class_name = None)", + "parameters": [ + { + "name": "routine", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "class_name", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Decorator that registers a function as a PETSc timing event.\n\nNo environment variables needed - works immediately!\n\nParameters\n----------\nroutine : callable\n Function or method to decorate\nclass_name : str, optional\n Class name for better event labeling (auto-detected for methods)\n\nReturns\n-------\ncallable\n Wrapped function that tracks calls via PETSc events\n\nExample\n-------\n>>> @uw.timing.routine_timer_decorator\n>>> def expensive_computation():\n>>> # ... complex calculations ...\n>>> return result\n>>>\n>>> uw.timing.start()\n>>> expensive_computation()\n>>> uw.timing.print_table() # Shows timing for expensive_computation\n\nNotes\n-----\n- First call registers the PETSc event (one-time cost)\n- Subsequent calls just increment counters (negligible overhead)\n- Events appear in PETSc log with full statistics", + "harvested_comments": [ + "... complex calculations ...", + "Shows timing for expensive_computation", + "Create event name", + "Register PETSc event (happens once per function)", + "Begin/end tracking - PETSc handles all statistics!" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "add_timing_to_module", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 395, + "signature": "(mod)", + "parameters": [ + { + "name": "mod", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Automatically add timing decorators to all classes and functions in a module.\n\nRecursively walks through a module, decorating all classes and their methods.\nUseful for comprehensive profiling of entire subsystems.\n\nParameters\n----------\nmod : module\n Python module to decorate\n\nExample\n-------\n>>> import underworld3 as uw\n>>> import underworld3.systems\n>>> uw.timing.add_timing_to_module(uw.systems) # Time all solver classes\n>>> uw.timing.start()\n>>> # ... use solvers ...\n>>> uw.timing.print_table() # See detailed solver timing\n\nNotes\n-----\n- Only decorates classes/functions defined in the specified module\n- Skips built-in modules and external dependencies\n- Safe to call multiple times (avoids double-decoration)", + "harvested_comments": [ + "Time all solver classes", + "... use solvers ...", + "See detailed solver timing", + "Already decorated", + "Find submodules to recurse into" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_event", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 473, + "signature": "(name)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a custom PETSc event for manual timing.\n\nUseful for timing specific code sections without decorators.\n\nParameters\n----------\nname : str\n Name for the event (appears in timing output)\n\nReturns\n-------\nPETSc.Log.Event\n Event object with begin() and end() methods\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>>\n>>> my_event = uw.timing.create_event(\"DataProcessing\")\n>>> my_event.begin()\n>>> # ... complex data processing ...\n>>> my_event.end()\n>>>\n>>> uw.timing.print_table() # Shows \"DataProcessing\" timing", + "harvested_comments": [ + "... complex data processing ...", + "Shows \"DataProcessing\" timing" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_summary", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 509, + "signature": "(filter_uw = True, min_time = 0.001, sort_by = 'time')", + "parameters": [ + { + "name": "filter_uw", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "min_time", + "type_hint": null, + "default": "0.001", + "description": "" + }, + { + "name": "sort_by", + "type_hint": null, + "default": "'time'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get user-friendly timing summary focusing on UW3 operations.\n\nFilters PETSc's comprehensive log to show only the most relevant timing\ninformation for UW3 users. By default, shows only UW3 operations (not\nlow-level PETSc internals).\n\nParameters\n----------\nfilter_uw : bool, optional\n If True (default), show only UW3 operations. If False, show all PETSc events.\nmin_time : float, optional\n Minimum time (seconds) for an event to be displayed. Default 0.001 (1ms).\n Helps filter out negligible operations.\nsort_by : str, optional\n Sort events by: 'time' (default), 'count', or 'name'.\n\nReturns\n-------\ndict\n Dictionary with keys:\n - 'events': List of (name, count, time, percent) tuples\n - 'total_time': Total execution time\n - 'num_events': Number of events displayed\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... do work ...\n>>>\n>>> # Get UW3-focused summary\n>>> summary = uw.timing.get_summary()\n>>> for name, count, time, pct in summary['events']:\n>>> print(f\"{name:40s} {count:5d} calls {time:8.3f}s ({pct:5.1f}%)\")\n>>>\n>>> # Get all events (including PETSc internals)\n>>> full_summary = uw.timing.get_summary(filter_uw=False, min_time=0.0)\n\nNotes\n-----\n- Call after `uw.timing.start()` and your computation\n- Filtered view helps identify UW3 performance bottlenecks\n- For comprehensive PETSc profiling, use `uw.timing.print_table()` or `filter_uw=False`", + "harvested_comments": [ + "... do work ...", + "Get UW3-focused summary", + "Get all events (including PETSc internals)", + "Collect all events first to calculate total time", + "UW3 event patterns (customize based on actual event naming)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "print_summary", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 623, + "signature": "(filter_uw = True, min_time = 0.001, sort_by = 'time', max_events = 50)", + "parameters": [ + { + "name": "filter_uw", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "min_time", + "type_hint": null, + "default": "0.001", + "description": "" + }, + { + "name": "sort_by", + "type_hint": null, + "default": "'time'", + "description": "" + }, + { + "name": "max_events", + "type_hint": null, + "default": "50", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Print user-friendly timing summary table.\n\nDisplays a clean, focused table of timing results for UW3 operations.\nMuch more readable than the full PETSc log for typical users.\n\nParameters\n----------\nfilter_uw : bool, optional\n If True (default), show only UW3 operations. If False, show all events.\nmin_time : float, optional\n Minimum time (seconds) for an event to be displayed. Default 0.001 (1ms).\nsort_by : str, optional\n Sort events by: 'time' (default), 'count', or 'name'.\nmax_events : int, optional\n Maximum number of events to display. Default 50.\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... run simulation ...\n>>>\n>>> # Quick UW3-focused summary\n>>> uw.timing.print_summary()\n>>>\n>>> # Detailed view with all events\n>>> uw.timing.print_summary(filter_uw=False, max_events=100)\n>>>\n>>> # Show top 10 most-called operations\n>>> uw.timing.print_summary(sort_by='count', max_events=10)\n\nNotes\n-----\n- For full PETSc profiling details, use `uw.timing.print_table()`\n- This function focuses on high-level UW3 operations\n- Perfect for quick performance checks in notebooks", + "harvested_comments": [ + "... run simulation ...", + "Quick UW3-focused summary", + "Detailed view with all events", + "Show top 10 most-called operations", + "Only rank 0 prints" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_quantity", + "kind": "method", + "file": "src/underworld3/units.py", + "line": 60, + "signature": "(self, value, units)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a Pint quantity.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_PintHelper", + "is_public": true + }, + { + "name": "get_units", + "kind": "method", + "file": "src/underworld3/units.py", + "line": 66, + "signature": "(self, quantity)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get units from a Pint quantity.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_PintHelper", + "is_public": true + }, + { + "name": "check_dimensionality", + "kind": "method", + "file": "src/underworld3/units.py", + "line": 70, + "signature": "(self, q1, q2)", + "parameters": [ + { + "name": "q1", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "q2", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Check if two quantities have compatible dimensionality.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_PintHelper", + "is_public": true + }, + { + "name": "get_dimensionality", + "kind": "method", + "file": "src/underworld3/units.py", + "line": 74, + "signature": "(self, quantity)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get dimensionality of a quantity.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_PintHelper", + "is_public": true + }, + { + "name": "check_units_consistency", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 510, + "signature": "(*expressions) -> bool", + "parameters": [ + { + "name": "*expressions", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if multiple expressions have consistent units for addition/comparison.\n\nThis function validates that all provided expressions have the same dimensionality,\nwhich is required for addition, subtraction, and comparison operations.\n\nArgs:\n *expressions: Any number of expressions, quantities, or unit-aware objects\n\nReturns:\n bool: True if all expressions have consistent units, False otherwise\n\nRaises:\n DimensionalityError: If expressions have inconsistent units\n NoUnitsError: If some expressions have units and others don't\n\nExamples:\n >>> velocity1 = EnhancedMeshVariable(\"v1\", mesh, 2, units=\"m/s\")\n >>> velocity2 = EnhancedMeshVariable(\"v2\", mesh, 2, units=\"km/h\")\n >>> pressure = EnhancedMeshVariable(\"p\", mesh, 1, units=\"Pa\")\n\n >>> check_units_consistency(velocity1, velocity2) # True - both velocities\n >>> check_units_consistency(velocity1, pressure) # False - different dimensions", + "harvested_comments": [ + "True - both velocities", + "False - different dimensions", + "Extract units info from all expressions", + "Check if all have units or all don't have units", + "All are unitless - consistent" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_dimensionality", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 567, + "signature": "(expression) -> Optional[Any]", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "Optional[Any]", + "existing_docstring": "Get the dimensionality of an expression or quantity.\n\nArgs:\n expression: Expression, quantity, or unit-aware object\n\nReturns:\n Dimensionality representation (backend-specific) or None if no units\n\nExamples:\n >>> velocity = EnhancedMeshVariable(\"velocity\", mesh, 2, units=\"m/s\")\n >>> dims = get_dimensionality(velocity)\n >>> print(dims) # [length] / [time]", + "harvested_comments": [ + "[length] / [time]" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 591, + "signature": "(expression, simplify: bool = False) -> Optional[Any]", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "simplify", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": "Optional[Any]", + "existing_docstring": "Get the units of an expression or quantity.\n\nThis is the unified, public API for extracting units from any object type\n(variables, quantities, SymPy expressions, etc.). It replaces the previous\n`units_of()` function and the internal `function.unit_conversion.get_units()`\nfor a clean, single API surface.\n\nIMPORTANT: This function ALWAYS returns Pint Unit objects (or None), never strings.\nWe accept strings for INPUT (user convenience), but always return Pint objects.\n\nArgs:\n expression: Expression, quantity, or unit-aware object\n simplify: If True, simplify mixed units to base SI (default False).\n\nReturns:\n Pint Unit object or None if no units\n\nExamples:\n >>> velocity = uw.discretisation.MeshVariable(\"velocity\", mesh, 2, units=\"m/s\")\n >>> units = uw.get_units(velocity)\n >>> print(units) # \n\n >>> # Mixed units from composite expressions\n >>> th2 = uw.expression(\"th2\", ((2 * kappa * t_now))**0.5)\n >>> uw.get_units(th2) # megayear ** 0.5 * meter / second ** 0.5\n >>> uw.get_units(th2, simplify=True) # meter (simplified!)", + "harvested_comments": [ + "", + "Mixed units from composite expressions", + "megayear ** 0.5 * meter / second ** 0.5", + "meter (simplified!)", + "Optionally simplify mixed units to base SI units" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "non_dimensionalise", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 643, + "signature": "(expression, model = None) -> Any", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "model", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Convert expression to non-dimensional form using model reference scales.\n\nThis function uses dimensional analysis to compute appropriate scaling factors\nfrom the model's reference quantities, then divides the expression by those\nscales to produce dimensionless values. Dimensionality information is preserved\nto enable re-dimensionalization.\n\nProtocol-based approach works with:\n- MeshVariable/SwarmVariable (via .non_dimensional_value() method)\n- UWQuantity objects (extracts dimensionality, computes scale)\n- UnitAwareArray (extracts dimensionality from units)\n- Plain numbers (pass through unchanged)\n\nArgs:\n expression: Expression, quantity, or unit-aware object to non-dimensionalise\n model: Model instance with reference quantities (uses default if None)\n\nReturns:\n Non-dimensional value(s) with preserved dimensionality metadata\n\nRaises:\n NoUnitsError: If expression has no units and model has reference quantities\n ValueError: If model has no reference quantities\n\nExamples:\n >>> # With variables (uses existing method)\n >>> velocity_var = MeshVariable(\"v\", mesh, 2, units=\"m/s\")\n >>> nondim_v = non_dimensionalise(velocity_var)\n\n >>> # With UWQuantity\n >>> velocity_qty = uw.quantity(5.0, \"cm/year\")\n >>> nondim_qty = non_dimensionalise(velocity_qty, model)\n >>> # Result is dimensionless but remembers it was velocity\n\n >>> # With plain number (no model reference quantities)\n >>> plain_value = 2.5\n >>> result = non_dimensionalise(plain_value) # Returns 2.5", + "harvested_comments": [ + "With variables (uses existing method)", + "With UWQuantity", + "Result is dimensionless but remembers it was velocity", + "With plain number (no model reference quantities)", + "Returns 2.5" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "show_nondimensional_form", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 887, + "signature": "(expression, model = None)", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "model", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Display the non-dimensionalized form of a complex expression.\n\nThis function unwraps the expression (expanding UW wrappers to SymPy),\napplies non-dimensional scaling, and returns the result for inspection.\nUseful for seeing what will actually be compiled into C code when\nscaling is active.\n\nArgs:\n expression: Any SymPy expression, UW expression, or variable\n model: Model instance with reference quantities (uses default if None)\n\nReturns:\n SymPy expression with non-dimensional scaling applied\n\nExamples:\n >>> # See what the Stokes flux looks like non-dimensionally\n >>> flux = stokes.constitutive_model.flux\n >>> nondim_flux = uw.show_nondimensional_form(flux)\n >>> print(nondim_flux) # Should show values close to 1.0, not 1e21\n\n >>> # Check a parameter\n >>> viscosity = model.Parameters.shear_viscosity_0\n >>> print(uw.show_nondimensional_form(viscosity)) # Should be ~1.0", + "harvested_comments": [ + "See what the Stokes flux looks like non-dimensionally", + "Should show values close to 1.0, not 1e21", + "Check a parameter", + "Should be ~1.0", + "Check if scaling is active" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "dimensionalise", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 933, + "signature": "(expression, target_dimensionality = None, model = None) -> Any", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "target_dimensionality", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "model", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Restore dimensional form to non-dimensional values using model reference scales.\n\nThis is the companion function to non_dimensionalise(). It multiplies dimensionless\nvalues by the appropriate reference scale to restore their dimensional form.\n\nThe function can operate in two modes:\n1. **Auto mode**: Extract dimensionality from the expression itself (if preserved)\n2. **Explicit mode**: Use provided target_dimensionality\n\nParameters\n----------\nexpression : UWQuantity, UnitAwareArray, or number\n Non-dimensional value with preserved dimensionality metadata.\ntarget_dimensionality : dict, optional\n Target dimensionality in Pint format, e.g.\n ``{'[length]': 1, '[time]': -1}`` for velocity.\n If None, uses dimensionality from the expression.\nmodel : Model, optional\n Model instance with reference quantities. Uses default if None.\n\nReturns\n-------\nquantity\n Dimensional quantity with appropriate units.\n\nRaises\n------\nValueError\n If no dimensionality information is available.\nValueError\n If model has no reference quantities.\n\nExamples\n--------\nAuto mode -- dimensionality preserved from ``non_dimensionalise()``:\n\n>>> velocity_qty = uw.quantity(5.0, \"cm/year\")\n>>> nondim_vel = non_dimensionalise(velocity_qty, model)\n>>> dimensional_vel = dimensionalise(nondim_vel, model=model)\n\nExplicit mode -- specify dimensionality:\n\n>>> plain_value = 2.5\n>>> velocity_dimensionality = {'[length]': 1, '[time]': -1}\n>>> velocity = dimensionalise(plain_value, velocity_dimensionality, model)", + "harvested_comments": [ + "Get model if not provided", + "IDEMPOTENCY CHECK: If input already has dimensional units, return as-is", + "This prevents double-conversion (e.g., 2900 km \u2192 2900000 km)", + "Check if units are already dimensional (not \"dimensionless\")", + "Already dimensional - idempotent return" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "simplify_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1092, + "signature": "(expression) -> Any", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Simplify and cancel units in an expression.\n\nThis function performs dimensional analysis to simplify unit expressions,\ncanceling common factors and reducing to fundamental units.\n\nArgs:\n expression: Expression with units to simplify\n\nReturns:\n Expression with simplified units\n\nExamples:\n >>> # Force per area = pressure\n >>> force_per_area = force / area # N/m\u00b2\n >>> simplified = simplify_units(force_per_area) # Pa", + "harvested_comments": [ + "Force per area = pressure", + "This would need backend-specific implementation", + "For now, return the original expression" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "create_quantity", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1121, + "signature": "(value, units: Union[str, Any], backend: Optional[str] = None) -> Any", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": "Union[str, Any]", + "default": null, + "description": "" + }, + { + "name": "backend", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Create a dimensional quantity from a value and units.\n\n.. deprecated:: 2026-07\n ``uw.quantity`` is THE quantity factory (maintainer decision D14):\n it returns a :class:`~underworld3.function.quantities.UWQuantity`,\n which composes with expressions and non-dimensionalisation. This\n function returns a raw Pint ``Quantity`` and will be removed after one\n deprecation cycle.\n\nArgs:\n value: Numeric value or array\n units: Units specification (string or units object)\n backend: Backend to use ('pint' or 'sympy'), defaults to 'pint'\n\nReturns:\n Dimensional quantity (raw Pint ``Quantity``)\n\nExamples:\n >>> velocity_qty = create_quantity([1.0, 2.0], \"m/s\")\n >>> pressure_qty = create_quantity(101325, \"Pa\")", + "harvested_comments": [ + "backend parameter is deprecated - Pint is the only supported backend" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "convert_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1155, + "signature": "(quantity, target_units: Union[str, Any]) -> Any", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "target_units", + "type_hint": "Union[str, Any]", + "default": null, + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Convert quantity to different units.\n\nThis is the SINGLE SOURCE OF TRUTH for unit conversion in UW3.\nAll .to() methods on unit-aware types should delegate to this function.\n\nHandles:\n- UWQuantity \u2192 returns new UWQuantity with converted value\n- UWexpression \u2192 returns new UWexpression with converted value\n- UnitAwareArray \u2192 returns new UnitAwareArray with converted values\n- Pint Quantity \u2192 returns converted Pint Quantity\n\nArgs:\n quantity: Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint)\n target_units: Target units for conversion (str or Pint Unit)\n\nReturns:\n Same type as input, converted to target units\n\nRaises:\n DimensionalityError: If units are not compatible for conversion\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> velocity = uw.quantity(10, \"m/s\")\n >>> velocity_kmh = uw.convert_units(velocity, \"km/h\")\n >>> print(velocity_kmh) # 36.0 [kilometer / hour]\n\n >>> radius = uw.expression(\"r\", uw.quantity(6370, \"km\"))\n >>> radius_m = uw.convert_units(radius, \"m\")\n >>> print(radius_m.value) # 6370000.0", + "harvested_comments": [ + "36.0 [kilometer / hour]", + "Get current units info", + "Parse target units if string", + "Check dimensional compatibility", + "Create test quantities to check compatibility" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "to_base_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1289, + "signature": "(quantity) -> Any", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Convert quantity to SI base units.\n\nThis is a convenience function that converts any unit-aware quantity\nto its SI base unit representation.\n\nArgs:\n quantity: Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint)\n\nReturns:\n Same type as input, converted to SI base units\n\nRaises:\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> velocity = uw.quantity(36, \"km/h\")\n >>> velocity_si = uw.to_base_units(velocity)\n >>> print(velocity_si) # 10.0 [meter / second]\n\n >>> radius = uw.expression(\"r\", uw.quantity(6370, \"km\"))\n >>> radius_si = uw.to_base_units(radius)\n >>> print(radius_si.value) # 6370000.0", + "harvested_comments": [ + "10.0 [meter / second]", + "Get current units info", + "=== Handle different input types ===", + "1. UWQuantity - use its native .to_base_units() method", + "2. UWexpression - convert the wrapped value" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "to_reduced_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1383, + "signature": "(quantity) -> Any", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Simplify units by canceling common factors.\n\nThis is useful for complex unit expressions like (kg * m / s\u00b2) / (kg / m\u00b3)\nwhich would simplify to m\u2074 / s\u00b2.\n\nArgs:\n quantity: Quantity to simplify (UWQuantity, UWexpression, UnitAwareArray, Pint)\n\nReturns:\n Same type as input, with simplified units\n\nRaises:\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> force = uw.quantity(100, \"kg*m/s**2\")\n >>> force_simplified = uw.to_reduced_units(force)\n >>> print(force_simplified) # 100.0 [newton]", + "harvested_comments": [ + "100.0 [newton]", + "Get current units info", + "=== Handle different input types ===", + "1. UWQuantity - use its native .to_reduced_units() method", + "2. UWexpression - convert the wrapped value" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "to_compact", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1469, + "signature": "(quantity) -> Any", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "Any", + "existing_docstring": "Convert quantity to most human-readable unit representation.\n\nThis automatically chooses unit prefixes (kilo, mega, micro, etc.)\nto make the number more readable. For example, 0.001 km becomes 1 m.\n\nArgs:\n quantity: Quantity to make compact (UWQuantity, UWexpression, UnitAwareArray, Pint)\n\nReturns:\n Same type as input, with compact units\n\nRaises:\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> length = uw.quantity(0.001, \"km\")\n >>> length_compact = uw.to_compact(length)\n >>> print(length_compact) # 1.0 [meter]\n\n >>> big_length = uw.quantity(1e9, \"m\")\n >>> big_compact = uw.to_compact(big_length)\n >>> print(big_compact) # 1.0 [gigameter]", + "harvested_comments": [ + "1.0 [meter]", + "1.0 [gigameter]", + "Get current units info", + "=== Handle different input types ===", + "1. UWQuantity - use its native .to_compact() method" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "get_scaling_coefficients", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1559, + "signature": "() -> Dict[str, Any]", + "parameters": [], + "returns": "Dict[str, Any]", + "existing_docstring": "Get the current scaling coefficients used for non-dimensionalisation.\n\nReturns:\n Dictionary of scaling coefficients for fundamental dimensions\n\nExamples:\n >>> coeffs = get_scaling_coefficients()\n >>> print(coeffs['[length]']) # 1.0 meter\n >>> print(coeffs['[time]']) # 1.0 year", + "harvested_comments": [ + "Use the existing scaling module" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "set_scaling_coefficients", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1577, + "signature": "(coefficients: Dict[str, Any]) -> None", + "parameters": [ + { + "name": "coefficients", + "type_hint": "Dict[str, Any]", + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Set custom scaling coefficients for non-dimensionalisation.\n\nArgs:\n coefficients: Dictionary mapping dimension names to scaling quantities\n\nExamples:\n >>> coeffs = get_scaling_coefficients()\n >>> coeffs['[length]'] = create_quantity(1000, \"km\") # Geological scale\n >>> coeffs['[time]'] = create_quantity(1e6, \"year\") # Geological time\n >>> set_scaling_coefficients(coeffs)", + "harvested_comments": [ + "Geological scale", + "Geological time", + "This would need to update the scaling module's global coefficients" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "is_dimensionless", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1595, + "signature": "(expression) -> bool", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if expression is dimensionless.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "has_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1600, + "signature": "(expression) -> bool", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if expression has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "same_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1605, + "signature": "(expr1, expr2) -> bool", + "parameters": [ + { + "name": "expr1", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "expr2", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if two expressions have the same units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "is_velocity", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1613, + "signature": "(expression) -> bool", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if expression has velocity dimensions [length]/[time].", + "harvested_comments": [ + "This would need backend-specific dimensionality checking", + "For now, check string representation" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "is_pressure", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1623, + "signature": "(expression) -> bool", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Check if expression has pressure dimensions [mass]/([length]\u22c5[time]\u00b2).", + "harvested_comments": [ + "This would need backend-specific dimensionality checking" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "validate_expression_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1633, + "signature": "(expression, expected_units: Union[str, Any]) -> bool", + "parameters": [ + { + "name": "expression", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "expected_units", + "type_hint": "Union[str, Any]", + "default": null, + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Validate that an expression has the expected units.\n\nArgs:\n expression: Expression to validate\n expected_units: Expected units (string or units object)\n\nReturns:\n True if units match, False otherwise\n\nRaises:\n NoUnitsError: If expression has no units but units are expected", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "assert_dimensionality", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1658, + "signature": "(value, expected_dimensionality: str, value_name: str = 'value', allow_dimensionless: bool = True, strict: bool = False) -> None", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "expected_dimensionality", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "value_name", + "type_hint": "str", + "default": "'value'", + "description": "" + }, + { + "name": "allow_dimensionless", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "strict", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Assert that a value has the expected dimensionality.\n\nThis is a general type-safety gatekeeper that validates physical dimensionality\nat various points in the code. Complements get_dimensionality() by providing\nenforcement rather than just inspection.\n\nArgs:\n value: The value to validate (quantity, expression, variable, array, etc.)\n expected_dimensionality: Expected dimensionality as a string\n - Specific dimensionality: \"[length]\", \"[length]/[time]\", \"[mass]*[length]/[time]**2\"\n - Dimensionless: \"dimensionless\" or \"\"\n value_name: Name of the value being validated (for error messages)\n allow_dimensionless: If True, accept dimensionless values even when dimensional\n expected (default: True, as dimensionless is valid for solver operations)\n strict: If True, raise error on dimensionless when dimensional expected\n (default: False, overrides allow_dimensionless)\n\nRaises:\n DimensionalityError: If dimensionality doesn't match expected\n NoUnitsError: If strict=True and value is dimensionless when dimensional expected\n\nExamples:\n >>> # Validate coordinates have length dimensionality\n >>> coords = mesh.X.coords\n >>> assert_dimensionality(coords, \"[length]\", \"coordinates\")\n\n >>> # Validate velocity has correct dimensionality\n >>> velocity = uw.discretisation.MeshVariable(\"v\", mesh, 2, units=\"m/s\")\n >>> assert_dimensionality(velocity, \"[length]/[time]\", \"velocity\")\n\n >>> # Validate pressure\n >>> pressure = uw.quantity(1e5, \"Pa\")\n >>> assert_dimensionality(pressure, \"[mass]/([length]*[time]**2)\", \"pressure\")\n\n >>> # Accept dimensionless when dimensional expected (default)\n >>> dimensionless_coords = np.array([[0, 1], [1, 1]])\n >>> assert_dimensionality(dimensionless_coords, \"[length]\", \"coords\") # OK\n\n >>> # Strict mode: reject dimensionless when dimensional expected\n >>> assert_dimensionality(\n ... dimensionless_coords, \"[length]\", \"coords\", strict=True\n ... ) # Raises NoUnitsError", + "harvested_comments": [ + "Validate coordinates have length dimensionality", + "Validate velocity has correct dimensionality", + "Validate pressure", + "Accept dimensionless when dimensional expected (default)", + "Strict mode: reject dimensionless when dimensional expected" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "validate_coordinates_dimensionality", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1789, + "signature": "(coords) -> None", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Validate that coordinates have length dimensionality [L].\n\nThis function checks that the provided coordinates are either dimensionless\n(which is valid for solver operations) or have length dimensionality. It will\nraise an error if coordinates have the wrong dimensionality (e.g., time,\ntemperature, velocity).\n\nArgs:\n coords: Coordinate array to validate\n\nRaises:\n DimensionalityError: If coordinates have units but not length dimensionality\n\nExamples:\n >>> # Valid: dimensionless coords (for solvers)\n >>> validate_coordinates_dimensionality(np.array([[0, 1], [1, 1]]))\n\n >>> # Valid: coords with length units\n >>> coords = uw.function.UnitAwareArray([[0, 1000], [1000, 1000]], units=\"meter\")\n >>> validate_coordinates_dimensionality(coords)\n\n >>> # Invalid: coords with time units (would raise error)\n >>> time_coords = uw.quantity(5.0, \"second\")\n >>> validate_coordinates_dimensionality(time_coords) # Raises DimensionalityError", + "harvested_comments": [ + "Valid: dimensionless coords (for solvers)", + "Valid: coords with length units", + "Invalid: coords with time units (would raise error)", + "Raises DimensionalityError", + "Check if coords have units" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "enforce_units_consistency", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1844, + "signature": "(*expressions) -> None", + "parameters": [ + { + "name": "*expressions", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Enforce units consistency, raising an error if inconsistent.\n\nArgs:\n *expressions: Expressions that must have consistent units\n\nRaises:\n DimensionalityError: If units are inconsistent\n NoUnitsError: If some have units and others don't", + "harvested_comments": [ + "This already raises appropriate errors" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "require_units_if_active", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1864, + "signature": "(value, name: str, expected_dimensionality: str = None, default_unit: str = None) -> float", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "expected_dimensionality", + "type_hint": "str", + "default": "None", + "description": "" + }, + { + "name": "default_unit", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": "float", + "existing_docstring": "Validate input and return nondimensional value when units are active.\n\nThis is a gatekeeper function for mesh creation and similar contexts where:\n- When Model with units is active: inputs MUST have units (enforces explicitness)\n- When no units active: raw numbers are accepted\n\nThis prevents ambiguity in dimensional problems where a raw number like `400`\ncould mean meters, kilometers, or a nondimensional value.\n\nArgs:\n value: Input value (quantity, expression, or raw number)\n name: Parameter name for error messages (e.g., \"depth_max\")\n expected_dimensionality: Expected dimensionality string (e.g., \"[length]\")\n If provided, validates the input has correct dimensionality.\n default_unit: Default unit to assume for raw values when units not active.\n Only used for documentation in error messages.\n\nReturns:\n float: Nondimensional value (divided by appropriate reference scale)\n\nRaises:\n ValueError: If units are active but value has no units\n DimensionalityError: If value has wrong dimensionality\n\nExamples:\n >>> # With units active - requires quantity\n >>> model = uw.Model()\n >>> model.set_reference_quantities(length=uw.quantity(1000, \"km\"), ...)\n >>> depth = require_units_if_active(\n ... uw.quantity(400, \"km\"),\n ... \"depth_max\",\n ... expected_dimensionality=\"[length]\"\n ... )\n >>> depth # Returns 0.4 (400 km / 1000 km reference)\n\n >>> # Without units - raw numbers accepted\n >>> depth = require_units_if_active(400, \"depth_max\")\n >>> depth # Returns 400 (unchanged)\n\n >>> # Error case - units active but raw number provided\n >>> model.set_reference_quantities(...)\n >>> require_units_if_active(400, \"depth_max\") # Raises ValueError", + "harvested_comments": [ + "With units active - requires quantity", + "Returns 0.4 (400 km / 1000 km reference)", + "Without units - raw numbers accepted", + "Returns 400 (unchanged)", + "Error case - units active but raw number provided" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "convert_angle_to_degrees", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 1964, + "signature": "(value, name: str = 'angle') -> float", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "'angle'", + "description": "" + } + ], + "returns": "float", + "existing_docstring": "Convert an angle value to degrees.\n\nAccepts:\n- Raw numbers: assumed to be degrees\n- Quantities with degree units: extracted as degrees\n- Quantities with radian units: converted to degrees\n\nArgs:\n value: Angle value (quantity or raw number)\n name: Parameter name for error messages\n\nReturns:\n float: Angle in degrees\n\nExamples:\n >>> convert_angle_to_degrees(45) # Raw number \u2192 45 degrees\n >>> convert_angle_to_degrees(uw.quantity(45, \"degree\")) # \u2192 45\n >>> convert_angle_to_degrees(uw.quantity(np.pi/4, \"radian\")) # \u2192 45", + "harvested_comments": [ + "Raw number \u2192 45 degrees", + "Raw number - assume degrees (conventional for geographic)", + "Has units - convert to degrees", + "pint-style quantity", + "UWQuantity with pint backend" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "uw_object_counter", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 460, + "signature": "(cls)", + "parameters": [], + "returns": null, + "existing_docstring": "Number of uw_object instances created", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "uw_object", + "is_public": true + }, + { + "name": "instance_number", + "kind": "property", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 465, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Unique number of the uw_object instance", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "uw_object", + "is_public": true + }, + { + "name": "is_notebook", + "kind": "function", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 25, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Detect if we're running in a Jupyter notebook.\n\nReturns True for:\n- Jupyter notebooks (ZMQInteractiveShell)\n- JupyterLab\n\nReturns False for:\n- Scripts run with `python script.py`\n- IPython terminal sessions\n- Batch jobs on HPC\n- pytest runs\n\nCan be overridden with environment variable:\n- UW_NOTEBOOK_EMULATION=1 forces notebook mode (pause will interrupt)\n- UW_NOTEBOOK_EMULATION=0 forces script mode (pause will be skipped)", + "harvested_comments": [ + "Environment variable override", + "Check for Jupyter notebook", + "ZMQInteractiveShell = Jupyter notebook / JupyterLab" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "pause", + "kind": "function", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 137, + "signature": "(message: str, explanation: str = None)", + "parameters": [ + { + "name": "message", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "explanation", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pause notebook execution for interactive review.\n\nIn Jupyter notebooks, this raises a UW_Pause exception that displays\na clean, formatted message.\n\nIn non-notebook environments (scripts, HPC jobs), this does nothing\nand execution continues - allowing the same notebook to run unattended.\n\nParameters\n----------\nmessage : str\n The pause message to display\nexplanation : str, optional\n Additional explanation for the user\n\nExamples\n--------\n>>> import underworld3 as uw\n\n>>> # This pauses in Jupyter, continues silently in scripts:\n>>> uw.pause(\"Mesh ready for inspection\")\n\n>>> # With an explanation:\n>>> uw.pause(\n... \"About to start expensive solve\",\n... explanation=\"This takes ~5 minutes on 4 cores\"\n... )\n\nEnvironment Variables\n---------------------\nUW_NOTEBOOK_EMULATION : str\n Override automatic detection:\n - \"1\" or \"true\": Emulate notebook mode (always pause)\n - \"0\" or \"false\": Emulate script mode (never pause)\n\nNotes\n-----\nThis is designed for development workflows where you want to:\n- Pause before expensive computations in notebooks\n- Review visualizations before continuing\n- Avoid pyvista issues when cells lack focus\n\nBut also want the same notebook to run unmodified on HPC.", + "harvested_comments": [ + "This pauses in Jupyter, continues silently in scripts:", + "With an explanation:" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "get_cache_dir", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 39, + "signature": "() -> Optional[Path]", + "parameters": [], + "returns": "Optional[Path]", + "existing_docstring": "Return the active JIT cache directory, or ``None`` if disabled.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "load_module", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 101, + "signature": "(source_hash: str, modname: str, constants_manifest)", + "parameters": [ + { + "name": "source_hash", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "modname", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "constants_manifest", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Try to load a cached compiled module for ``source_hash``.\n\nParameters\n----------\nsource_hash : str\n The hex hash of the canonical C source (same key the in-memory\n cache uses).\nmodname : str\n The fully-qualified module name to register with Python's import\n machinery (typically ``fn_ptr_ext_``).\nconstants_manifest : list of (int, UWexpression)\n The current call's constants manifest. Used as a belt-and-braces\n consistency check: the saved manifest on disk must match, otherwise\n we treat it as a cache miss (probable ABI drift).\n\nReturns\n-------\nmodule or None\n The dynamically-loaded extension module on hit, ``None`` on miss\n (no ``.so`` on disk, malformed manifest, or manifest mismatch).", + "harvested_comments": [ + "Names shifted between the saved entry and the current call.", + "Could be a UWexpression rename or unnoticed ABI change \u2014 safer", + "to recompile than trust a stale mapping." + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "store_module", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 154, + "signature": "(source_hash: str, modname: str, tmpdir, constants_manifest) -> None", + "parameters": [ + { + "name": "source_hash", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "modname", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "tmpdir", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "constants_manifest", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Copy a freshly-compiled ``.so`` into the cache dir and write its manifest.\n\nParameters\n----------\nsource_hash, modname\n See :func:`load_module`.\ntmpdir : str or Path\n The temporary build directory returned by :func:`compile_and_load`\n (contains the ``.so`` we just built).\nconstants_manifest : list of (int, UWexpression)\n Persisted as ``{index, name}`` pairs; used by :func:`load_module`\n to verify compatibility on subsequent loads.", + "harvested_comments": [ + "Phase 4 will replace this guard with proper MPI locking.", + "flock prevents a concurrent process (different mpirun, etc.) from", + "interleaving its write with ours. The recheck-inside-lock pattern", + "avoids redundant copies when another process has already populated", + "the entry while we were waiting on the lock." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "flat", + "kind": "method", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 230, + "signature": "(self) -> tuple", + "parameters": [], + "returns": "tuple", + "existing_docstring": "Concatenate all slots into a single ordered tuple.\n\nThe ordering (residual, bcs, jacobian, bd_residual, bd_jacobian)\nmatches what ``_createext()`` expects.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "JITCallbackSet", + "is_public": true + }, + { + "name": "signature", + "kind": "method", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 238, + "signature": "(self) -> tuple", + "parameters": [], + "returns": "tuple", + "existing_docstring": "Hashable key that preserves callback role separation.\n\nTwo callback sets with the same expressions in different roles\nwill produce different signatures.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "JITCallbackSet", + "is_public": true + }, + { + "name": "map", + "kind": "method", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 246, + "signature": "(self, fn) -> 'JITCallbackSet'", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": "'JITCallbackSet'", + "existing_docstring": "Apply *fn* to every expression in every slot, returning a new set.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "JITCallbackSet", + "is_public": true + }, + { + "name": "counts", + "kind": "property", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 257, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Lengths of each slot, for ``_createext()`` offset calculation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "JITCallbackSet", + "is_public": true + }, + { + "name": "prepare_for_cache_key", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 263, + "signature": "(fn, constants_subs_map)", + "parameters": [ + { + "name": "fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "constants_subs_map", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Prepare a single expression for JIT cache hashing.\n\nTwo-phase process:\n1. Substitute constant UWexpressions with ``_JITConstant`` placeholders\n so that changing a constant's *value* does not invalidate the cache.\n2. Unwrap remaining (non-constant) UWexpressions to pure SymPy so the\n hash is deterministic.\n\nParameters\n----------\nfn : sympy expression or None\n The expression to expand.\nconstants_subs_map : dict or None\n Mapping from UWexpression symbols to ``_JITConstant`` placeholders.", + "harvested_comments": [ + "Phase 1: Substitute constants with _JITConstant placeholders", + "Phase 2: Unwrap remaining (non-constant) expressions" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "getext", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 538, + "signature": "(mesh, callbacks: JITCallbackSet, primary_field_list, verbose = False, debug = False, debug_name = None, cache = True)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "callbacks", + "type_hint": "JITCallbackSet", + "default": null, + "description": "" + }, + { + "name": "primary_field_list", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "debug", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "debug_name", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "cache", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compile (or retrieve cached) JIT extension for PETSc pointwise functions.\n\nParameters\n----------\nmesh : Mesh\n Supporting mesh for coordinate system and variable information.\ncallbacks : JITCallbackSet\n Callback expressions grouped by PETSc role (residual, bcs, jacobian,\n bd_residual, bd_jacobian).\nprimary_field_list : iterable\n Variables that map to PETSc primary arrays (``petsc_u[]``).\n All others map to auxiliary arrays (``petsc_a[]``).\n\nReturns\n-------\nGextResult\n Named tuple with fields (ptrobj, fn_dicts, constants_manifest).\n constants_manifest is a list of (index, uw_expression_ref) tuples\n for use with PetscDSSetConstants().", + "harvested_comments": [ + "Extract constant UWexpressions that are routed through PETSc's", + "constants[] array. Value changes don't affect the C source \u2014 they", + "only alter what we pass to PetscDSSetConstants at solve time.", + "Generate C source. The returned ``gen_modname``/``gen_randstr`` are", + "implementation-dependent (often random) \u2014 we canonicalise them below" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "generate_c_source", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 748, + "signature": "(name, mesh: underworld3.discretisation.Mesh, callbacks: JITCallbackSet, primary_field_list, constants_subs_map: Optional[dict] = None, verbose: Optional[bool] = False, debug: Optional[bool] = False, debug_name = None)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "underworld3.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "callbacks", + "type_hint": "JITCallbackSet", + "default": null, + "description": "" + }, + { + "name": "primary_field_list", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "constants_subs_map", + "type_hint": "Optional[dict]", + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "debug", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "debug_name", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Generate the setup.py / C header / Cython wrapper for a JIT bundle.\n\nThis is the pure text-generation phase: sympy processing, C-code emission,\nand assembly of the files that will make up the compiled module. No I/O,\nno subprocess, no dynamic loading \u2014 those happen in ``compile_and_load``.\n\nKeying a cache on a hash of the generated C source requires that this\nfunction produce byte-identical output for byte-identical inputs.\n\nParameters\n----------\nname : str or int\n Identifier used to build ``MODNAME = \"fn_ptr_ext_\" + str(name)``.\nmesh : Mesh\ncallbacks : JITCallbackSet\nprimary_field_list : list\n Variables that map to PETSc primary variable arrays (``petsc_u[]``).\nconstants_subs_map : dict, optional\n Mapping from UWexpression \u2192 ``_JITConstant`` placeholder.\n\nReturns\n-------\nmodname : str\n Fully-qualified extension module name (``fn_ptr_ext_``).\ncodeguys : list of [filename, content]\n The files that make up the source bundle\n (``setup.py``, ``cy_ext.h``, ``cy_ext.pyx``).\ndiagnostics : dict\n Equation-range counts and the random symbol prefix, used by the caller\n for verbose printing and for building the fn-layout manifest.", + "harvested_comments": [ + "`_ccode` patching", + "variable increment", + "variable gradient increment", + "monkey patch this guy into the function", + "Now patch the gradient components. The gradient of a" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "compile_and_load", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 1282, + "signature": "(modname, codeguys, verbose = False)", + "parameters": [ + { + "name": "modname", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "codeguys", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Write ``codeguys`` files to a temp directory, build with Cython, and dynamically load.\n\nSplit out of the former ``_createext`` so that generation and compilation\ncan be hashed/cached independently.\n\nParameters\n----------\nmodname : str\n Fully-qualified name of the extension module (``fn_ptr_ext_``).\ncodeguys : list of [filename, content]\n Source files from :func:`generate_c_source`.\nverbose : bool, optional\n Print build diagnostics on failure.\n\nReturns\n-------\nmodule : loaded Python extension module exposing ``getptrobj()``.\ntmpdir : str\n Location of the generated sources, useful when a caller wants to\n persist the ``.so`` elsewhere (e.g. a cross-session disk cache).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "summary", + "kind": "method", + "file": "src/underworld3/utilities/_params.py", + "line": 423, + "signature": "(self, title: str = 'Parameters')", + "parameters": [ + { + "name": "title", + "type_hint": "str", + "default": "'Parameters'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Print a compact table of current parameter settings.\n\nUses uw.pprint so only rank 0 prints in parallel.\n\nArgs:\n title: Header line for the table.\n\nExample output::\n\n Parameters\n -----------------------------------------------\n eta_background 1e+24 Pa*s background viscosity\n eta_base 1e+21 Pa*s base (weak) viscosity\n convergence_rate 2.5 mm/yr convergence velocity\n problem_size 2 mesh resolution level\n smoothing 3e-05\n -----------------------------------------------", + "harvested_comments": [ + "Strip common prefix for display (e.g. \"uw_\" \u2192 cleaner names)", + "Build rows: (display_name, value_str, description)", + "Format value \u2014 use the Param's original units string if available", + "(e.g. \"Pa*s\") rather than Pint's verbose form (\"pascal * second\")", + "Source tag appended to description, not value" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Params", + "is_public": true + }, + { + "name": "to_dict", + "kind": "method", + "file": "src/underworld3/utilities/_params.py", + "line": 498, + "signature": "(self) -> dict", + "parameters": [], + "returns": "dict", + "existing_docstring": "Return parameters as a dictionary.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Params", + "is_public": true + }, + { + "name": "reset", + "kind": "method", + "file": "src/underworld3/utilities/_params.py", + "line": 502, + "signature": "(self, name: str = None)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Reset parameter(s) to default values.\n\nArgs:\n name: Parameter to reset, or None to reset all", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Params", + "is_public": true + }, + { + "name": "cli_help", + "kind": "method", + "file": "src/underworld3/utilities/_params.py", + "line": 522, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Return help text for command-line usage with type and units info.", + "harvested_comments": [ + "Rich parameter with type/units info", + "Build the help line", + "Add example for quantity params", + "Add bounds if specified", + "Add description if specified" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Params", + "is_public": true + }, + { + "name": "require_dirs", + "kind": "function", + "file": "src/underworld3/utilities/_petsc_tools.py", + "line": 9, + "signature": "(ListOfDirs)", + "parameters": [ + { + "name": "ListOfDirs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "List of directories required by this run", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "parse_cmd_line_options", + "kind": "function", + "file": "src/underworld3/utilities/_petsc_tools.py", + "line": 19, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "This function will parse all PETSc type command line options\nand pass them through via `petsc4py`.", + "harvested_comments": [ + "petsc options have single hyphen prefix", + "if it's the last item, set to None", + "if the next item is a different key, set to None", + "else set next item to the option value" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "get_installation_data", + "kind": "property", + "file": "src/underworld3/utilities/_utils.py", + "line": 98, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the installation data for the underworld3 installation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_uw_record", + "is_public": true + }, + { + "name": "get_runtime_data", + "kind": "property", + "file": "src/underworld3/utilities/_utils.py", + "line": 105, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the runtime data for the underworld3 installation.\nNote this requires a MPI broadcast to get the data.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "_uw_record", + "is_public": true + }, + { + "name": "mem_footprint", + "kind": "function", + "file": "src/underworld3/utilities/_utils.py", + "line": 186, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Returns resident set size in Mb for this process", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "gather_data", + "kind": "function", + "file": "src/underworld3/utilities/_utils.py", + "line": 196, + "signature": "(val, bcast = False, dtype = 'float64')", + "parameters": [ + { + "name": "val", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "bcast", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "'float64'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "gather values on root (bcast=False) or all (bcast = True) processors\nParameters:\n vals : Values to combine into a single array on the root or all processors\n\nreturns:\n val_global : combination of values form all processors", + "harvested_comments": [ + "## make sure all data comes in the same order", + "## Collect local array sizes using the high-level mpi4py gather", + "# gather x values, can't do them together", + "## remove rows with NaN", + "### make available on all processors" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "postHog", + "kind": "function", + "file": "src/underworld3/utilities/_utils.py", + "line": 259, + "signature": "(event_name, ev_dict)", + "parameters": [ + { + "name": "event_name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "ev_dict", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Posts an Event Tracking message to PostHog.\n\nCurrent Underworld3 only dispatches an event when the underworld module\nis imported. This is effected by the calling of this function from\nunderworld/__init__.py.\n\nPostHog uses distinct_id in ev_dict to determine unique users. In Underworld this is\ngenerated by a random string at install and record it in _uwid.py (the\nvalue is available via the uw._id attribute). If the file (_uwid.py) exists, it\nis not recreated, so generally it will only be created the first time you build\nunderworld. As this is a 'per build' identifier, it means that all users of a\nparticular docker image will be identified as the same GA user. Likewise, all\nusers of a particular HPC Underworld module will also be identified as the same\nuser.\nRegarding HPC usage, it seems that the compute nodes on most machines are closed\nto external network access, and hence POST requests will not be dispatched\nsuccessfully. Unfortunately this means that most high proc count simulations\nwill not be captured in this data.\n\nThis 'trojan' is via PostHog service. It can be disabled by setting the environment\nvariable `UW_NO_USAGE_METRICS`. Note, this function will return quietly on any errors.\n\nParameters\n----------\nevent_name: str\n Textual name for event_name. Can only contain alpha-numeric characters and underscores.\nev_dict: dict\n Optional parameter dictionary for event.", + "harvested_comments": [ + "build payload for PostHog comms", + "# debugging", + "print(f\"The would-be payload:\\n{payload}\\n\")", + "print(f\"request.post status: {r.status_code}\")" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "boundary_flux", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 201, + "signature": "(solver, boundary, mass = 'lumped', remove_mean = False, normal = None)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mass", + "type_hint": null, + "default": "'lumped'", + "description": "" + }, + { + "name": "remove_mean", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "normal", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "See ``SolverBaseClass.boundary_flux``. Returns ``(xs, flux)`` for this rank's\nboundary nodes; scalar solver \u2192 normal flux, vector solver \u2192 traction (or its normal\ncomponent if ``normal`` is given).", + "harvested_comments": [ + "vector reaction (traction sigma.n at each node)", + "scalar NORMAL component sigma_nn = n.(sigma.n)", + "full traction vector: de-smear each component independently" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "write_boundary_scalar_field", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 231, + "signature": "(solver, field, value_by_key, dim)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "value_by_key", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Write ``value_by_key`` (coordinate-key \u2192 scalar) onto a scalar MeshVariable\n``field`` at the matching nodes; interior nodes untouched. Returns ``field``.\n\nThe field is written ONCE from a local numpy copy: a per-node write to ``var.data``\nfires the variable's write-callback each time, and the boundary-node count differs per\nrank (a rank may own none of the boundary), so per-node writes would desync the\ncallback's collective and deadlock. Shared by the boundary-flux and rotated-free-slip\n(dynamic topography) field hand-offs.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "boundary_flux_field", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 256, + "signature": "(solver, boundary, field, mass = 'lumped', remove_mean = False, scale = 1.0, normal = None)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mass", + "type_hint": null, + "default": "'lumped'", + "description": "" + }, + { + "name": "remove_mean", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "scale", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "normal", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "See ``SolverBaseClass.boundary_flux_field`` (the documented entry point;\nthis free function is its implementation and shares its name). Writes\n``scale * flux`` onto the scalar MeshVariable ``field`` at the boundary\nnodes (interior untouched).\n\n``scale`` is a generic multiplier on the recovered flux. For dynamic\ntopography it is the **negated reciprocal** of the buoyancy scale used by\nthe expression-return paths: ``scale = -1 / buoyancy_scale``, where\n``buoyancy_scale`` is :math:`\\Delta\\rho\\, g` as taken by\n``dynamic_topography`` / ``topography`` (there the division and the minus\nsign are internal). The two parameters are deliberately NOT aliased \u2014\ntreating them as one factor invites sign/reciprocal errors.", + "harvested_comments": [ + "a SCALAR field can only hold a scalar flux \u2014 a vector solver returns a per-node", + "traction VECTOR unless a `normal` is given to project it. Fail fast rather than", + "silently pairing the flattened vector with the nodes." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "boundary_flux_to_field", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 286, + "signature": "(*args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Deprecated alias for :func:`boundary_flux_field` (renamed 2026-07 so the\nfree function matches the solver method it implements; kept one cycle).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_dmplex_from_medit", + "kind": "function", + "file": "src/underworld3/utilities/create_dmplex_from_medit.py", + "line": 17, + "signature": "(medit_file, print_info = False)", + "parameters": [ + { + "name": "medit_file", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "print_info", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Reads a medit (.mesh) file and returns a distributed DMPlex with labels.", + "harvested_comments": [ + "Read mesh data", + "Label Tetrahedra", + "Define boundary labels", + "PETSc requires all ranks to call getBoundingBox()", + "Label points" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "barycentric_prolongation", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 51, + "signature": "(coarse_coords, fine_coords)", + "parameters": [ + { + "name": "coarse_coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fine_coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "FE-exact prolongation: each fine DOF interpolated by the coarse element\nthat contains it (barycentric weights). Partition of unity (row sums = 1);\nfine points outside the coarse hull fall back to the nearest coarse DOF.", + "harvested_comments": [ + "outside coarse hull \u2192 nearest coarse DOF" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "rbf_prolongation", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 78, + "signature": "(coarse_coords, fine_coords, smooth = 0.0)", + "parameters": [ + { + "name": "coarse_coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fine_coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "smooth", + "type_hint": null, + "default": "0.0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "RBF prolongation: polyharmonic (r\u00b2 log r) kernel + affine polynomial tail\n(reproduces linear fields), Shepard row-normalised to a partition of unity.\nWorks for arbitrary (non-nested) point sets; software-equivalent to the\nbarycentric builder as an MG transfer operator.", + "harvested_comments": [ + "affine tail", + "Solve M X\u1d40 = B\u1d40 rather than forming M\u207b\u00b9 explicitly (faster, more stable). M is", + "symmetric, so B M\u207b\u00b9 = solve(M, B\u1d40)\u1d40." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "sbr_refine", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 154, + "signature": "(dm, cells)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cells", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "SBR-refine an explicit list of ``cells``.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "sbr_refine_where", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 162, + "signature": "(dm, predicate)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "predicate", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "SBR-refine cells whose centroid satisfies ``predicate(centroid)``.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "build", + "kind": "method", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 536, + "signature": "(self, solver)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Build the BC-reduced prolongations. ``solver`` is the (built) finest\nsolver. Each COARSE level's BC-constrained reduced map is derived directly\nfrom the coarse mesh DM by copying the finest solver's fields + DS onto it\n(no throwaway solver); the finest level reads its map from ``solver.dm``.\n\nSerial: scipy reduced->reduced CSR. Parallel (np>1): rank-local node-level\nweights assembled into MPIAIJ transfers with global-section reduced\nnumbering (nested co-partitioned path).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "CustomMGHierarchy", + "is_public": true + }, + { + "name": "set_custom_fmg", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 605, + "signature": "(solver, coarse_meshes)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coarse_meshes", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Generalized custom-P FMG with BC-per-level reduction (the correct path).\n\nRegisters a :class:`CustomMGHierarchy` on the solver so that the next\n``solve()`` builds and installs it (build-time injection). The hierarchy is\n``[*coarse_meshes, solver.mesh]``; each coarse level's BC-constrained reduced\nmap is derived directly from its DM by copying the solver's fields + DS\n(``_coarse_reduced_map``), so ``coarse_meshes`` need only carry the same\nboundary labels as the solver's mesh. For a saddle-point (Stokes) solver pass\n``field_id=0`` to target the velocity sub-block.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "inject_custom_mg", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 625, + "signature": "(solver)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Build + install the custom-P FMG. Called from ``solve()`` (after ``_build``,\nbefore the SNES solve) when ``solver._custom_mg`` is set. Dispatches:\n- ``mode == \"hierarchy\"`` -> BC-per-level reduced path (correct, general);\n- legacy dict ``{coarse_meshes, kind}`` -> finest-only reduction (kept for\n back-compat; valid only when coarse levels are non-nested / unconstrained).", + "harvested_comments": [ + "parallel-capable (nested co-partitioned)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_petsc_library_match", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 118, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Check if the PETSc library used at compile time matches runtime.\n\nThis is the most common source of cryptic errors like:\n\"DMInterpolationSetUp_UW failed with error 98\"", + "harvested_comments": [ + "Get petsc4py's view of where PETSc is", + "Find our extension module", + "Also check Linux naming", + "Get libraries the extension links to", + "Find PETSc library reference" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_petsc_version", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 235, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Check PETSc version compatibility.", + "harvested_comments": [ + "Check minimum version" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_petsc_version_match", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 265, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Check if the PETSc version at compile time matches runtime version.\n\nThis detects the common issue where underworld3 was built against\nPETSc 3.X but is running with PETSc 3.Y, causing cryptic errors like:\n\"Must call SNESSetFunction() or SNESSetDM() before SNESComputeFunction()\"\n\nThe check works by:\n1. Finding the libpetsc.X.Y.dylib that the extension links to\n2. Comparing X.Y to the runtime petsc4py version", + "harvested_comments": [ + "Get runtime PETSc version", + "Find an extension module that links to PETSc", + "Prefer _function or petsc_discretisation as they definitely link to PETSc", + "Get linked libraries", + "Find the PETSc library and extract version from filename" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_extension_modules", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 385, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Check that all required extension modules load correctly.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_mpi_configuration", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 423, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Check MPI is configured correctly.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_basic_evaluation", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 445, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Test basic function evaluation - this catches PETSc ABI mismatches.\n\nThis is the \"smoke test\" that would have caught the error 98 issue.", + "harvested_comments": [ + "Create minimal test", + "This is the critical test - evaluate at a point" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "check_environment_variables", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 514, + "signature": "() -> DiagnosticResult", + "parameters": [], + "returns": "DiagnosticResult", + "existing_docstring": "Check relevant environment variables.", + "harvested_comments": [ + "Check if this is a pixi-managed AMR environment", + "In AMR environments, PETSC_DIR is intentionally set to petsc-custom via pixi activation", + "Check for potential issues - but not for intentional pixi AMR setups", + "This is intentional - AMR environments use custom PETSc", + "Not a pixi-managed setup - could be a genuine conflict" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "run_diagnostics", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 568, + "signature": "(verbose: bool = True, quick: bool = False) -> List[DiagnosticResult]", + "parameters": [ + { + "name": "verbose", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "quick", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": "List[DiagnosticResult]", + "existing_docstring": "Run all diagnostic checks.\n\nParameters\n----------\nverbose : bool\n Print results as they're generated\nquick : bool\n Skip slow tests (like basic evaluation)\n\nReturns\n-------\nList[DiagnosticResult]\n Results from all checks", + "harvested_comments": [ + "Status symbols", + "Green checkmark", + "Yellow warning", + "Check if terminal supports colors" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "doctor", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 655, + "signature": "(verbose: bool = True, quick: bool = False) -> bool", + "parameters": [ + { + "name": "verbose", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "quick", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": "bool", + "existing_docstring": "Run diagnostics and return True if all checks pass.\n\nThis is the main entry point for users.\n\nParameters\n----------\nverbose : bool\n Print detailed output (default True)\nquick : bool\n Skip slow tests like function evaluation (default False)\n\nReturns\n-------\nbool\n True if no errors found, False otherwise\n\nExamples\n--------\n>>> import underworld3 as uw\n>>> uw.doctor() # Run full diagnostics\n>>> uw.doctor(quick=True) # Quick check without evaluation test", + "harvested_comments": [ + "Run full diagnostics", + "Quick check without evaluation test" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "health_check", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 684, + "signature": "() -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Quick silent health check. Returns True if OK.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 33, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get dimensionality from units if available.\nReturns None for dimensionless quantities.", + "harvested_comments": [ + "Non-dimensional has no dimensionality", + "Try to get from Pint if using UWQuantity", + "Use Pint directly to get dimensionality", + "For simple string units (fallback)", + "Dimensionless" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "scaling_coefficient", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 57, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the reference scale for non-dimensionalization", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "scaling_coefficient", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 62, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the reference scale for non-dimensionalization", + "harvested_comments": [ + "Handle UWQuantity or Pint quantities", + "Convert to same units as self if possible" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "is_nondimensional", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 82, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if currently in non-dimensional state", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "nd_array", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 87, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get non-dimensional array values.\nConvenience property for accessing array data in non-dimensional form.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "from_nd", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 99, + "signature": "(self, nd_value)", + "parameters": [ + { + "name": "nd_value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert a non-dimensional value back to dimensional form.\n\nArgs:\n nd_value: Non-dimensional value to convert\n\nReturns:\n Dimensional value", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "set_reference_scale", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 111, + "signature": "(self, scale)", + "parameters": [ + { + "name": "scale", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the reference scale for non-dimensionalization.\n\nArgs:\n scale: Reference scale (can be UWQuantity, Pint quantity, or number)", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "DimensionalityMixin", + "is_public": true + }, + { + "name": "array", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 164, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return non-dimensionalized array values", + "harvested_comments": [ + "Get the underlying numpy array and scale it" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "array", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 173, + "signature": "(self, values)", + "parameters": [ + { + "name": "values", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set values in non-dimensional form (converts back to dimensional)", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "data", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 178, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return non-dimensionalized data values", + "harvested_comments": [ + "Get the underlying numpy array and scale it" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "data", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 187, + "signature": "(self, values)", + "parameters": [ + { + "name": "values", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set data in non-dimensional form", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "sym", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 192, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return non-dimensional symbolic representation", + "harvested_comments": [ + "Create starred version" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 205, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Non-dimensional quantities have no dimensionality", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 210, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Non-dimensional quantities have no units", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "is_nondimensional", + "kind": "property", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 215, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Always True for non-dimensional view", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "to_dimensional", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 219, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Convert back to dimensional form", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NonDimensionalView", + "is_public": true + }, + { + "name": "detect_format", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 35, + "signature": "(docstring: str) -> DocstringFormat", + "parameters": [ + { + "name": "docstring", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "DocstringFormat", + "existing_docstring": "Detect whether docstring is NumPy/Sphinx or Markdown format.\n\nParameters\n----------\ndocstring : str\n The docstring to analyze.\n\nReturns\n-------\nDocstringFormat\n The detected format.", + "harvested_comments": [ + "NumPy format indicators (check these first - more specific)", + "Markdown format indicators", + "Inline math $...$ (not $$)", + "Display math $$...$$", + "Code blocks" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "parse_numpy_docstring", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 82, + "signature": "(docstring: str) -> ParsedDocstring", + "parameters": [ + { + "name": "docstring", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "ParsedDocstring", + "existing_docstring": "Parse a NumPy-format docstring into components.\n\nParameters\n----------\ndocstring : str\n NumPy-formatted docstring.\n\nReturns\n-------\nParsedDocstring\n Parsed components.", + "harvested_comments": [ + "Split into sections", + "Pattern matches section headers like \"Parameters\\n----------\"", + "First part is summary + extended description", + "Process named sections" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "parse_markdown_docstring", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 179, + "signature": "(docstring: str) -> ParsedDocstring", + "parameters": [ + { + "name": "docstring", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "ParsedDocstring", + "existing_docstring": "Parse a Markdown-format docstring into components.\n\nParameters\n----------\ndocstring : str\n Markdown-formatted docstring.\n\nReturns\n-------\nParsedDocstring\n Parsed components.", + "harvested_comments": [ + "For markdown, we primarily preserve structure", + "Split on **Section**: style headers", + "First non-empty line is summary", + "Look for **Arguments**: or **Parameters**: sections", + "Parse \"- `name`: description\" or \"- name: description\"" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "numpy_to_markdown", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 232, + "signature": "(parsed: ParsedDocstring) -> str", + "parameters": [ + { + "name": "parsed", + "type_hint": "ParsedDocstring", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Convert parsed NumPy docstring to Markdown for Jupyter display.\n\nTransforms:\n- :math:`x` -> $x$\n- .. math:: blocks -> $$...$$ blocks\n- Parameters section -> formatted list\n\nParameters\n----------\nparsed : ParsedDocstring\n Parsed docstring components.\n\nReturns\n-------\nstr\n Markdown-formatted string.", + "harvested_comments": [ + "Extended description" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "markdown_to_numpy", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 295, + "signature": "(text: str) -> str", + "parameters": [ + { + "name": "text", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Convert Markdown docstring to NumPy format.\n\nTransforms:\n- $x$ -> :math:`x`\n- $$...$$ -> .. math:: blocks\n\nParameters\n----------\ntext : str\n Markdown-formatted docstring.\n\nReturns\n-------\nstr\n NumPy/RST-formatted docstring.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "in_jupyter", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 389, + "signature": "() -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Detect if running in Jupyter environment.\n\nReturns\n-------\nbool\n True if in Jupyter, False otherwise.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "markdown_to_plain", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 409, + "signature": "(text: str) -> str", + "parameters": [ + { + "name": "text", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Convert markdown to plain text for terminal display.\n\nParameters\n----------\ntext : str\n Markdown-formatted text.\n\nReturns\n-------\nstr\n Plain text with formatting stripped.", + "harvested_comments": [ + "Remove bold/italic", + "Convert math to plain", + "Remove code block markers", + "Convert bullet points" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "render_docstring", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 444, + "signature": "(docstring: str, target: str = 'auto') -> str", + "parameters": [ + { + "name": "docstring", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "target", + "type_hint": "str", + "default": "'auto'", + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Render a docstring for the specified target.\n\nParameters\n----------\ndocstring : str\n The docstring to render.\ntarget : str\n Output target: \"jupyter\", \"terminal\", \"rst\", or \"auto\".\n \"auto\" detects the environment.\n\nReturns\n-------\nstr\n Rendered docstring.", + "harvested_comments": [ + "Auto-detect target", + "Detect source format", + "Already markdown or unknown - return as-is", + "Already NumPy/RST format" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "points_in_simplex2D", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 13, + "signature": "(p, a, b, c)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "c", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "p - numpy array of points in 3D\na, b, c - triangle points (numpy 1x2 arrays)\n\nreturns:\n numpy array of truth values for each of the points in p (is this point in the triangle)", + "harvested_comments": [ + "Compute dot products", + "Compute barycentric coordinates" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "distance_pointcloud_triangle", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 80, + "signature": "(p, a, b, c)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "c", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "p - numpy array of points in 3D\na, b, c - triangle points (numpy 1x3 arrays)\n\nreturns:\n numpy array of distances from each of the points to the nearest point within the triangle (0 if in the plane, within the triangle)", + "harvested_comments": [ + "There are multiple branches so we will have to mask to find those.", + "The first / general case is the perpendicular distance to the plane", + "of the triangle.", + "If outside the prism defined by the triangle extruded along its normal,", + "over-write the near point appropriately" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "distance_pointcloud_linesegment", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 160, + "signature": "(p, a, b)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "p - numpy array of points\na, b - line-segment points (numpy 1xdim arrays)\n\nreturns:\n numpy array of distances from each of the points to the nearest point within the triangle (0 if in the plane, within the triangle)", + "harvested_comments": [ + "Three different cases:", + "1: P < 0 return distance p to a", + "2: P > 1 return distance p to b", + "3: 0 <= P <= 1 return perpendicular distance", + "First, perpendicular distance for every point (case 3)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "signed_distance_pointcloud_linesegment_2d", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 211, + "signature": "(p, a, b)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "a", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "b", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute signed distance from 2D points to a line segment.\n\nThe sign is determined by which side of the line the point is on:\n- Positive: to the \"left\" of the segment (when looking from a to b)\n- Negative: to the \"right\" of the segment\n\nParameters\n----------\np : numpy array (n, 2)\n Query points in 2D\na : numpy array (2,) or (1, 2)\n Start point of line segment\nb : numpy array (2,) or (1, 2)\n End point of line segment\n\nReturns\n-------\nnumpy array (n,)\n Signed distances from each point to the line segment", + "harvested_comments": [ + "Compute unsigned distance", + "Compute sign using 2D cross product (z-component of 3D cross)", + "cross(ab, ap) = ab_x * ap_y - ab_y * ap_x", + "Sign: positive if point is to the left of ab", + "Handle points exactly on the line" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "linesegment_normals_2d", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 254, + "signature": "(points)", + "parameters": [ + { + "name": "points", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute unit normals for line segments defined by ordered points (2D).\n\nFor a segment from points[i] to points[i+1], the normal points to the\n\"left\" side (90\u00b0 counterclockwise rotation of the tangent).\n\nParameters\n----------\npoints : numpy array (n, 2)\n Ordered vertices defining n-1 line segments\n\nReturns\n-------\nsegment_normals : numpy array (n-1, 2)\n Unit normal for each segment\nvertex_normals : numpy array (n, 2)\n Unit normal at each vertex (averaged from adjacent segments)", + "harvested_comments": [ + "Compute tangent vectors for each segment", + "Rotate 90\u00b0 counterclockwise: (x, y) -> (-y, x)", + "Avoid division by zero for degenerate segments", + "Compute vertex normals by averaging adjacent segment normals", + "First vertex: use first segment's normal" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "distance_pointcloud_polyline", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 311, + "signature": "(p, vertices)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vertices", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute distance from points to a polyline (multiple connected segments).\n\nParameters\n----------\np : numpy array (n, dim)\n Query points\nvertices : numpy array (m, dim)\n Ordered vertices defining the polyline (m-1 segments)\n\nReturns\n-------\nnumpy array (n,)\n Distance from each point to the nearest point on the polyline", + "harvested_comments": [ + "Compute distance to each segment", + "Return minimum distance across all segments" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "signed_distance_pointcloud_polyline_2d", + "kind": "function", + "file": "src/underworld3/utilities/geometry_tools.py", + "line": 347, + "signature": "(p, vertices)", + "parameters": [ + { + "name": "p", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vertices", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute signed distance from 2D points to a polyline.\n\nThe sign is determined by the nearest segment:\n- Positive: to the \"left\" of the polyline (consistent orientation)\n- Negative: to the \"right\" of the polyline\n\nParameters\n----------\np : numpy array (n, 2)\n Query points in 2D\nvertices : numpy array (m, 2)\n Ordered vertices defining the polyline (m-1 segments)\n\nReturns\n-------\nnumpy array (n,)\n Signed distance from each point to the polyline", + "harvested_comments": [ + "Compute signed distance to each segment", + "Find the segment with minimum absolute distance for each point", + "Return the signed distance to the nearest segment" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "diff", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 184, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Direct differentiation with automatic unit wrapping.\n\nReturns a unit-aware expression if the original variable has units,\nplain SymPy expression otherwise.\n\nFor Matrix/vector results, creates a custom wrapper that provides\nunit-aware indexing, so derivative[0] returns a unit-aware object.\n\nExamples:\n temperature.diff(y)[0] # Returns unit-aware dT/dy with .units and .to()\n velocity[0].diff(x) # Returns unit-aware dVx/dx with unit methods\n velocity.diff(x) # Returns UnitAwareDerivativeMatrix with unit-aware elements", + "harvested_comments": [ + "Returns unit-aware dT/dy with .units and .to()", + "Returns unit-aware dVx/dx with unit methods", + "Returns UnitAwareDerivativeMatrix with unit-aware elements", + "Unwrap any UnitAwareExpression arguments to their SymPy expressions", + "It's a UnitAwareExpression - extract the SymPy expression" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MathematicalMixin", + "is_public": true + }, + { + "name": "norm", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 601, + "signature": "(self, norm_type = None)", + "parameters": [ + { + "name": "norm_type", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Compute the norm of the variable.\n\nThis method intelligently delegates to either SymPy or PETSc:\n- If called without arguments: Uses SymPy Matrix norm (for mathematical expressions)\n- If called with arguments: Uses PETSc vector norm (for computational operations)\n\nParameters\n----------\nnorm_type : int, optional\n PETSc norm type (0=NORM_1, 2=NORM_2, 3=NORM_INFINITY)\n If None, uses SymPy Matrix norm (defaults to 2-norm)\n\nReturns\n-------\nsympy.Expr or float/tuple\n If norm_type is None: SymPy expression for the norm\n If norm_type is provided: PETSc norm value (float or tuple for multi-component)\n\nExamples\n--------\n>>> vel.norm() # Mathematical: returns SymPy sqrt(v_x^2 + v_y^2)\n>>> vel.norm(2) # Computational: returns PETSc L2 norm value", + "harvested_comments": [ + "Mathematical: returns SymPy sqrt(v_x^2 + v_y^2)", + "Computational: returns PETSc L2 norm value", + "Mathematical usage: delegate to SymPy Matrix.norm()", + "SymPy norm defaults to 2-norm (Euclidean)", + "Computational usage: delegate to PETSc norm (via super())" + ], + "status": "complete", + "needs": [], + "parent_class": "MathematicalMixin", + "is_public": true + }, + { + "name": "sym_repr", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 635, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mathematical representation of the variable.\n\nShows the symbolic form that would be used in mathematical\nexpressions and JIT compilation.\n\nReturns:\n String representation of the symbolic form\n\nExample:\n velocity = MeshVariable(\"velocity\", mesh, 2)\n velocity # Shows computational view\n velocity.sym_repr() # Shows: \"Matrix([[V_0(x, y, z)], [V_1(x, y, z)]])\"", + "harvested_comments": [ + "Shows computational view", + "Shows: \"Matrix([[V_0(x, y, z)], [V_1(x, y, z)]])\"" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MathematicalMixin", + "is_public": true + }, + { + "name": "to", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 652, + "signature": "(self, target_units: str)", + "parameters": [ + { + "name": "target_units", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert to different units.\n\nDEPRECATED (2025-11-26): Unit conversion on variables now returns scaled SymPy expression.\nFor unit-aware results, wrap in uw.expression() first.\n\nParameters\n----------\ntarget_units : str\n Target units to convert to (e.g., 'km/s', 'degC')\n\nReturns\n-------\nsympy.Expr\n Scaled SymPy expression\n\nNotes\n-----\nFollowing the Transparent Container Principle, this returns a raw SymPy\nexpression with the scaling factor applied. Use uw.get_units() to derive\nunits from the result.", + "harvested_comments": [ + "Get current units", + "Compute scaling factor using Pint", + "Compute conversion factor", + "Check for offset units (like Celsius)", + "Return scaled SymPy expression" + ], + "status": "complete", + "needs": [], + "parent_class": "MathematicalMixin", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 828, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if this derivative matrix has units (for protocol compatibility).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 833, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the units of this derivative matrix, accounting for derivatives.", + "harvested_comments": [ + "Compute units once and cache", + "Get the first element to check if it's a derivative", + "Check if this is a derivative expression (has diffindex)", + "This is a derivative! Compute derivative units", + "Use source units if available (from the original variable)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 882, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the dimensionality of this derivative matrix.", + "harvested_comments": [ + "Use Pint directly to get dimensionality" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": true + }, + { + "name": "to", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 894, + "signature": "(self, target_units: str)", + "parameters": [ + { + "name": "target_units", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert derivative matrix to different units.\n\nDEPRECATED (2025-11-26): Returns scaled SymPy matrix.\n\nParameters\n----------\ntarget_units : str\n Target units to convert to\n\nReturns\n-------\nsympy.MatrixBase\n Scaled SymPy matrix expression", + "harvested_comments": [ + "Get current units (already computed correctly by self.units property)", + "Convert to Pint units if string", + "Parse target units", + "Check dimensionality compatibility", + "Compute conversion factor" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": true + }, + { + "name": "shape", + "kind": "property", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 987, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Pass through shape attribute.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": true + }, + { + "name": "diff", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 995, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Differentiate the derivative matrix, unwrapping UnitAwareExpression arguments.\n\nThis allows chained differentiation like temperature.diff(x).diff(y)\nwhere x and y are UnitAwareExpression objects.", + "harvested_comments": [ + "Unwrap any UnitAwareExpression arguments to their SymPy expressions", + "It's a UnitAwareExpression - extract the SymPy expression", + "It's a variable with .sym property", + "Regular SymPy object or symbol", + "Wrap the result with units" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": true + }, + { + "name": "test_mathematical_mixin_fixed", + "kind": "function", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 1027, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Test the fixed mathematical mixin implementation.", + "harvested_comments": [ + "Create test symbols", + "Test scalar variable", + "Test vector variable", + "Should raise clear error", + "Should work with default arguments" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "enable", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 69, + "signature": "() -> None", + "parameters": [], + "returns": "None", + "existing_docstring": "Turn instrumentation on at runtime.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "disable", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 75, + "signature": "() -> None", + "parameters": [], + "returns": "None", + "existing_docstring": "Turn instrumentation off at runtime.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "snapshot", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 146, + "signature": "(full: bool = False) -> dict[str, Any]", + "parameters": [ + { + "name": "full", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": "dict[str, Any]", + "existing_docstring": "Capture a memory snapshot.\n\nParameters\n----------\nfull\n If ``True``, also walk ``gc.get_objects()`` to count live Python\n instances by class (slow). Defaults to ``False``.\n\nReturns\n-------\ndict\n Keys: ``rss_mb``, ``kdtree`` (mapping with ``live`` and\n ``total_constructed``), ``py_classes`` (mapping; only present when\n ``full=True``).", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "diff", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 171, + "signature": "(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any]", + "parameters": [ + { + "name": "before", + "type_hint": "dict[str, Any]", + "default": null, + "description": "" + }, + { + "name": "after", + "type_hint": "dict[str, Any]", + "default": null, + "description": "" + } + ], + "returns": "dict[str, Any]", + "existing_docstring": "Compute a structured diff between two snapshots.\n\nReturns deltas only (excludes anything unchanged or absent). Python\nclass entries are sorted by absolute change so the dominant suspects\nappear first.", + "harvested_comments": [ + "Threshold below the format precision (0.01 MiB \u2248 10 KiB): smaller", + "noise \u2014 inevitable when the source is RSS in KiB pages \u2014 would print", + "as \"+0.00 MiB\" and defeat the \"no change\" fast path." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "format_diff", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 209, + "signature": "(label: str, delta: dict[str, Any]) -> str", + "parameters": [ + { + "name": "label", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "delta", + "type_hint": "dict[str, Any]", + "default": null, + "description": "" + } + ], + "returns": "str", + "existing_docstring": "Render a diff for stdout / logs. One line for trivial deltas, more if needed.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "probe", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 229, + "signature": "(label: str, full: bool = False)", + "parameters": [ + { + "name": "label", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "full", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Context manager: snapshot before, snapshot after, log the diff.\n\nParameters\n----------\nlabel\n Short identifier for the block, included in the emitted diff.\nfull\n Walk ``gc.get_objects()`` for per-class deltas (slow).\nemit\n Callable receiving the formatted diff string. Defaults to\n :func:`print`. Use a logger or rank-aware writer for parallel runs.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "instrument", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 253, + "signature": "(label: str, full: bool = False)", + "parameters": [ + { + "name": "label", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "full", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Decorator wrapping a method/function with :func:`probe`.\n\nFast-returns when :data:`ENABLED` is ``False`` (sub-microsecond overhead),\nso it's safe to leave permanent decorations on hot paths.\n\nExamples\n--------\n>>> @instrument(\"stokes-solve\")\n... def solve(self, ...):\n... ...", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "dump_petsc_leaks_at_finalize", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 276, + "signature": "(filename: str | None = None) -> None", + "parameters": [ + { + "name": "filename", + "type_hint": "str | None", + "default": "None", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Ask PETSc to dump unfreed objects at finalize.\n\nSets the runtime options ``malloc_dump`` and ``objects_dump`` so PETSc\nwrites its leak report when the process tears down. When ``filename``\nis given, also sets ``malloc_view`` to the same path for a more verbose\nallocation dump.\n\nNote that ``petsc4py``'s :class:`Options` API expects keys *without* the\nleading ``-`` that you'd type on the command line \u2014 same convention used\nelsewhere in this codebase.\n\nEquivalent to running with ``-malloc_dump -objects_dump`` on the command\nline. Useful when you can't change the launch command but can call this\nfrom Python early in the run.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "is_delaying", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 54, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if callbacks are currently being delayed.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DelayedCallbackManager", + "is_public": true + }, + { + "name": "push_delay_context", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 59, + "signature": "(self, context_info = None)", + "parameters": [ + { + "name": "context_info", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Enter a new delay context.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DelayedCallbackManager", + "is_public": true + }, + { + "name": "pop_delay_context", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 69, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Exit delay context and return callbacks accumulated in this context.", + "harvested_comments": [ + "Get callbacks from this context level", + "If we're exiting the outermost context, clear all callbacks", + "Remove only this context's callbacks (keep outer context callbacks)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DelayedCallbackManager", + "is_public": true + }, + { + "name": "add_delayed_callback", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 90, + "signature": "(self, array, callback_func, change_info)", + "parameters": [ + { + "name": "array", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "callback_func", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "change_info", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add a callback to the delayed execution queue.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DelayedCallbackManager", + "is_public": true + }, + { + "name": "mask", + "kind": "property", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 214, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Public mask property for numpy.ma compatibility.\n\nMatplotlib's quiver and other plotting functions access .mask directly.\nThis aliases to _mask which returns np.ma.nomask (no masking).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "mask", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 223, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Public mask setter for numpy.ma compatibility.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "filled", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 227, + "signature": "(self, fill_value = None)", + "parameters": [ + { + "name": "fill_value", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return array with masked values filled.\n\nFor numpy.ma compatibility. Since we have no mask, this just\nreturns a copy of the data (as numpy array to avoid further\nmasked array operations).\n\nParameters\n----------\nfill_value : scalar, optional\n Value used to fill masked entries. Ignored since we have no mask.\n\nReturns\n-------\nndarray\n A copy of the data as a plain numpy array.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "set_callback", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 289, + "signature": "(self, callback: Callable)", + "parameters": [ + { + "name": "callback", + "type_hint": "Callable", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set a single callback function (replaces any existing callbacks).\n\nParameters\n----------\ncallback : callable\n Function with signature: callback(array, change_info)\n - array: the NDArray_With_Callback instance\n - change_info: dict with operation details", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "add_callback", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 302, + "signature": "(self, callback: Callable)", + "parameters": [ + { + "name": "callback", + "type_hint": "Callable", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Add an additional callback function.\n\nParameters\n----------\ncallback : callable\n Function to add to callback list", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "remove_callback", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 314, + "signature": "(self, callback: Callable)", + "parameters": [ + { + "name": "callback", + "type_hint": "Callable", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Remove a specific callback function.\n\nParameters\n----------\ncallback : callable\n Function to remove from callback list", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "clear_callbacks", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 326, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Remove all registered callbacks.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "enable_callbacks", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 330, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Enable callback triggering.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "disable_callbacks", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 334, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Disable callback triggering (useful for batch operations).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "owner", + "kind": "property", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 339, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the owner object (may be None if owner was garbage collected).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "delay_callback", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 343, + "signature": "(self, context_info = None)", + "parameters": [ + { + "name": "context_info", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Context manager to delay callback execution until context exit.\n\nDuring the context, all callbacks from this array (and any other arrays\nusing delay_callback) will be accumulated and executed when the outermost\ncontext exits.\n\nParameters\n----------\ncontext_info : str, optional\n Optional information about the context (for debugging)\n\nExample\n-------\n>>> with arr.delay_callback(\"batch update\"):\n... arr[0] = 1\n... arr[1] = 2\n... arr[2] = 3\n# All callbacks fire here at context exit", + "harvested_comments": [ + "All callbacks fire here at context exit", + "MPI barrier to ensure all processes enter delay context together", + "Get callbacks accumulated during this context", + "MPI barrier to ensure all processes finish their delayed operations", + "before any process starts executing callbacks" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "delay_callbacks_global", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 415, + "signature": "(context_info = None)", + "parameters": [ + { + "name": "context_info", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Static method to create a global delay context for all NDArray_With_Callback instances.\n\nThis is useful when you don't have a specific array instance but want to delay\ncallbacks from multiple arrays.\n\nExample\n-------\n>>> with NDArray_With_Callback.delay_callbacks_global(\"mesh update\"):\n... mesh.data[0] = new_pos\n... swarm.data += displacement\n# All callbacks from all arrays fire here", + "harvested_comments": [ + "All callbacks from all arrays fire here", + "MPI barrier to ensure all processes enter delay context together", + "Get callbacks accumulated during this context", + "MPI barrier to ensure all processes finish their delayed operations", + "before any process starts executing callbacks" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "fill", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 755, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Fill array with scalar value, triggering callback.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "sort", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 765, + "signature": "(self, axis = -1, kind = None, order = None)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "-1", + "description": "" + }, + { + "name": "kind", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "order", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Sort array in-place, triggering callback.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "resize", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 775, + "signature": "(self, new_shape, refcheck = True)", + "parameters": [ + { + "name": "new_shape", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "refcheck", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Resize array in-place, triggering callback.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "copy", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 787, + "signature": "(self, order = 'C')", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "'C'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a copy of the array.\n\nThe copy will have the same callbacks registered but will be independent.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 800, + "signature": "(self, dtype = None, type = None)", + "parameters": [ + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "type", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a view of the array.\n\nViews share callbacks with the original array.", + "harvested_comments": [ + "Use numpy's ndarray.view directly to avoid recursion", + "Simple view with same type and dtype", + "View with different dtype, then cast to our type", + "Use specified type (may not be our type)", + "Copy our attributes to the result if it's our type" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "sync_data", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 827, + "signature": "(self, new_data)", + "parameters": [ + { + "name": "new_data", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Update array with new data, preserving callbacks and all metadata.\n\nThis method efficiently handles both same-size and different-size data updates.\nFor same-size updates, it uses efficient in-place copying. For different sizes,\nit creates a new array object but preserves all metadata and callbacks.\n\nParameters\n----------\nnew_data : array-like\n New data to sync into this array. Can be different size/shape.\n\nReturns\n-------\nresult : NDArray_With_Callback\n For same-size: returns self (same object)\n For different-size: returns new object with same metadata\n\nNotes\n-----\n- For same-size data: Uses efficient in-place copy (preserves object identity)\n- For different sizes: Creates new object but copies all callbacks/metadata\n- All callbacks, owner references, and settings are preserved\n- Triggers 'sync_data' callback after update\n\nExamples\n--------\n>>> arr = NDArray_With_Callback([1, 2, 3])\n>>> result = arr.sync_data([4, 5, 6]) # Same size: returns same object\n>>> assert result is arr\n>>> result = arr.sync_data([7, 8, 9, 10, 11]) # Different size: new object\n>>> assert result is not arr # Different object\n>>> assert len(result._callbacks) == len(arr._callbacks) # Same callbacks", + "harvested_comments": [ + "Same size: returns same object", + "Different size: new object", + "Different object", + "Same callbacks", + "Store old info for callback" + ], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_max", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 950, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return maximum across all MPI ranks.\n\nFor scalar results (axis=None), performs MPI reduction. For array results,\nperforms component-wise maximum.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nscalar or ndarray\n Global maximum value(s)", + "harvested_comments": [ + "Try to get underworld MPI comm, fall back to MPI.COMM_WORLD", + "Handle empty arrays (use -inf as identity for max)", + "Determine result shape for empty array", + "Scalar result - perform MPI reduction", + "Array result - component-wise reduction" + ], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_min", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 1023, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return minimum across all MPI ranks.\n\nFor scalar results (axis=None), performs MPI reduction. For array results,\nperforms component-wise minimum.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nscalar or ndarray\n Global minimum value(s)", + "harvested_comments": [ + "Handle empty arrays (use +inf as identity for min)", + "Scalar result", + "Array result" + ], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_sum", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 1094, + "signature": "(self, axis = None, dtype = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return sum across all MPI ranks.\n\nFor scalar results (axis=None), performs MPI reduction. For array results,\nperforms component-wise sum.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\ndtype : data-type, optional\n Type of returned array\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nscalar or ndarray\n Global sum value(s)", + "harvested_comments": [ + "Scalar result", + "Array result" + ], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_mean", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 1145, + "signature": "(self, axis = None, dtype = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return mean across all MPI ranks.\n\nComputes the true global mean by summing all values across ranks and\ndividing by total count.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\ndtype : data-type, optional\n Type of returned array\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nscalar or ndarray\n Global mean value(s)", + "harvested_comments": [ + "Get local count", + "Get global sum and count", + "Compute mean" + ], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_size", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 1194, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return total number of elements across all MPI ranks.\n\nUseful for computing global statistics that require total element count.\n\nReturns\n-------\nint\n Total number of elements summed across all MPI ranks", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_norm", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 1215, + "signature": "(self, ord = None)", + "parameters": [ + { + "name": "ord", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return 2-norm across all MPI ranks.\n\nComputes sqrt(sum of squares) across all ranks.\n\nParameters\n----------\nord : {None, 2}, optional\n Order of the norm (only 2-norm supported, default: None = 2-norm)\n\nReturns\n-------\nfloat\n Global 2-norm value", + "harvested_comments": [ + "Compute local sum of squares", + "Global sum of squares" + ], + "status": "complete", + "needs": [], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "global_rms", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 1253, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return root mean square across all MPI ranks.\n\nComputes RMS = sqrt(sum of squares / total count) across all ranks.\n\nReturns\n-------\nfloat\n Global RMS value", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": true + }, + { + "name": "derive_scaling_coefficients", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 12, + "signature": "(model)", + "parameters": [ + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Automatically derive scaling coefficients for all variables and parameters\nbased on the model's reference quantities.\n\nArgs:\n model: The Model object with reference quantities set\n\nThis function performs dimensional analysis to find the appropriate\ncombination of reference quantities that produces the correct\ndimensionality for each variable/parameter.", + "harvested_comments": [ + "No reference quantities to work with", + "Get fundamental scales from reference quantities", + "No fundamental scales derived yet", + "Process all registered variables", + "Try to derive scaling coefficient from dimensionality" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "get_required_reference_quantities", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 210, + "signature": "(units_str, reference_quantities = None)", + "parameters": [ + { + "name": "units_str", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "reference_quantities", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Determine which reference quantities are required for a given unit string.\n\nUses pure dimensional analysis - NOT hardcoded parameter names.\nChecks if the given unit's dimensionality can be derived from available\nreference quantities through dimensional analysis alone.\n\nArgs:\n units_str: Unit string (e.g., 'Pa', 'm/s', 'K')\n reference_quantities: Optional dict of reference quantities to check against\n\nReturns:\n tuple: (can_be_derived, descriptive message)\n can_be_derived=True if dimensionality can be derived from available quantities\n message contains explanation of what is available or missing", + "harvested_comments": [ + "Parse the target units", + "If we can't parse, don't validate", + "If no reference quantities provided, we can't determine if derivable", + "Extract dimensionalities from all reference quantities", + "Check if target dimensionality exactly matches any reference quantity" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "validate_variable_reference_quantities", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 337, + "signature": "(var_name, units_str, model)", + "parameters": [ + { + "name": "var_name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units_str", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Validate that a variable's units can be properly scaled by reference quantities.\n\nUses pure dimensional analysis (NOT hardcoded parameter names) to determine\nif the variable's dimensionality can be derived from available reference quantities.\n\nArgs:\n var_name: Name of the variable\n units_str: Unit string for the variable\n model: Model object to check for reference quantities\n\nReturns:\n tuple: (is_valid, warning_message)\n is_valid=True if units can be scaled properly\n warning_message contains details if validation fails", + "harvested_comments": [ + "Check if model has any reference quantities set", + "Use dimensional analysis to check if units can be derived", + "All good - dimensionality can be derived", + "Cannot derive the needed dimensionality" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "apply_nondimensional_scaling", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 446, + "signature": "(expr, model)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Apply non-dimensional scaling to a SymPy expression.\n\nThis replaces all variables and parameters with their non-dimensional\nequivalents by dividing by their scaling coefficients.\n\nArgs:\n expr: SymPy expression\n model: Model object with scaling coefficients set\n\nReturns:\n Scaled SymPy expression", + "harvested_comments": [ + "Find all function symbols (variables)", + "Try to find corresponding variable in model", + "Check if this function matches the variable's symbolic form", + "Create non-dimensional symbol", + "Replace with scaled version" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "is_buffer", + "kind": "function", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 29, + "signature": "(obj, mode)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Function to check if an object is a buffer and\nsupports the required mode (read/write)", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "open_file", + "kind": "function", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 38, + "signature": "(path_or_buf, mode = 'r')", + "parameters": [ + { + "name": "path_or_buf", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": "'r'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Context manager for opening a file or handling an existing buffer", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "read_medit_ascii", + "kind": "function", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 47, + "signature": "(filename, mesh_data_name)", + "parameters": [ + { + "name": "filename", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh_data_name", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Function to read a medit mesh file in ascii format.\nReturn mesh data and their indices if found.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "print_medit_mesh_info", + "kind": "function", + "file": "src/underworld3/utilities/read_medit_ascii.py", + "line": 57, + "signature": "(file_path)", + "parameters": [ + { + "name": "file_path", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "print medit mesh info", + "harvested_comments": [ + "End of file", + "Check for alphabetic characters in the first item", + "Process the MeshVersionFormatted line", + "Process the Dimension line", + "Print other lines that contain alphabetic characters" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "van_genuchten_Se", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 68, + "signature": "(psi, alpha, n, m = None)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "m", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Effective saturation (Van Genuchten model).\n\n.. math::\n\n S_e(\\psi) = \\begin{cases}\n \\left[1 + (\\alpha |\\psi|)^n\\right]^{-m} & \\psi < 0 \\\\\n 1 & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head (negative in unsaturated zone).\nalpha : float\n Inverse of the air-entry pressure [1/length].\nn : float\n Pore-size distribution parameter (n > 1).\nm : float, optional\n Van Genuchten parameter. Default: ``1 - 1/n``.\n\nReturns\n-------\nsympy.Piecewise\n Effective saturation expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "van_genuchten_theta", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 105, + "signature": "(psi, theta_r, theta_s, alpha, n, m = None)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "m", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Volumetric water content (Van Genuchten model).\n\n.. math::\n\n \\theta(\\psi) = \\theta_r + (\\theta_s - \\theta_r)\\, S_e(\\psi)\n\nParameters\n----------\npsi : sympy expression\n Pressure head.\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\nalpha : float\n Inverse of the air-entry pressure [1/length].\nn : float\n Pore-size distribution parameter.\nm : float, optional\n Default: ``1 - 1/n``.\n\nReturns\n-------\nsympy.Expr\n Water content expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "van_genuchten_K", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 138, + "signature": "(psi, Ks, alpha, n, m = None)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Ks", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "m", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Hydraulic conductivity (Van Genuchten\u2013Mualem model).\n\n.. math::\n\n K(\\psi) = \\begin{cases}\n K_s \\, S_e^{1/2}\n \\left[1 - \\left(1 - S_e^{1/m}\\right)^m\\right]^2\n & \\psi < 0 \\\\\n K_s & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head.\nKs : float\n Saturated hydraulic conductivity.\nalpha : float\n Inverse of the air-entry pressure [1/length].\nn : float\n Pore-size distribution parameter.\nm : float, optional\n Default: ``1 - 1/n``.\n\nReturns\n-------\nsympy.Piecewise\n Hydraulic conductivity expression.", + "harvested_comments": [ + "For the unsaturated branch, extract the unsaturated Se expression" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "van_genuchten_C", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 187, + "signature": "(psi, theta_r, theta_s, alpha, n, m = None, Ss = 0.0)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "m", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "Ss", + "type_hint": null, + "default": "0.0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Specific moisture capacity (Van Genuchten model).\n\n.. math::\n\n C(\\psi) = \\frac{d\\theta}{d\\psi} = \\begin{cases}\n \\alpha\\, m\\, n\\, (\\theta_s - \\theta_r)\\,\n (\\alpha |\\psi|)^{n-1}\\,\n \\left[1 + (\\alpha |\\psi|)^n\\right]^{-(m+1)}\n & \\psi < 0 \\\\\n S_s & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head.\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\nalpha : float\n Inverse of the air-entry pressure [1/length].\nn : float\n Pore-size distribution parameter.\nm : float, optional\n Default: ``1 - 1/n``.\nSs : float, optional\n Specific storage for the saturated zone (default 0).\n Set to a small positive value (e.g. 1e-4) to avoid\n a singular mass matrix when the domain is fully saturated.\n\nReturns\n-------\nsympy.Piecewise\n Specific moisture capacity expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "gardner_K", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 251, + "signature": "(psi, Ks, alpha)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Ks", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Hydraulic conductivity (Gardner exponential model).\n\n.. math::\n\n K(\\psi) = \\begin{cases}\n K_s \\exp(\\alpha\\,\\psi) & \\psi < 0 \\\\\n K_s & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head (negative in unsaturated zone).\nKs : float\n Saturated hydraulic conductivity.\nalpha : float\n Sorptive number [1/length]. Larger values give a\n sharper transition near saturation.\n\nReturns\n-------\nsympy.Piecewise\n Hydraulic conductivity expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "gardner_theta", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 283, + "signature": "(psi, theta_r, theta_s, alpha)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Volumetric water content (Gardner exponential model).\n\n.. math::\n\n \\theta(\\psi) = \\begin{cases}\n \\theta_r + (\\theta_s - \\theta_r)\\,\\exp(\\alpha\\,\\psi)\n & \\psi < 0 \\\\\n \\theta_s & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head.\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\nalpha : float\n Sorptive number [1/length].\n\nReturns\n-------\nsympy.Piecewise\n Water content expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "gardner_C", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 318, + "signature": "(psi, theta_r, theta_s, alpha, Ss = 0.0)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Ss", + "type_hint": null, + "default": "0.0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Specific moisture capacity (Gardner exponential model).\n\n.. math::\n\n C(\\psi) = \\frac{d\\theta}{d\\psi} = \\begin{cases}\n \\alpha\\,(\\theta_s - \\theta_r)\\,\\exp(\\alpha\\,\\psi)\n & \\psi < 0 \\\\\n S_s & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head.\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\nalpha : float\n Sorptive number [1/length].\nSs : float, optional\n Specific storage for the saturated zone (default 0).\n\nReturns\n-------\nsympy.Piecewise\n Specific moisture capacity expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "gardner_steady_state_psi", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 356, + "signature": "(y, psi_0, psi_L, L, alpha)", + "parameters": [ + { + "name": "y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "psi_0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "psi_L", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "L", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Analytical steady-state pressure head for Gardner model with gravity.\n\nFor a 1D vertical column of height *L* with the Gardner conductivity\nmodel, steady-state Richards equation with gravity reduces to\n\n.. math::\n\n K(\\psi)\\left(\\frac{d\\psi}{dy} + 1\\right) = q = \\text{const}\n\nThe substitution :math:`u = \\exp(\\alpha\\psi)` linearises the ODE.\nThe exact solution with boundary conditions\n:math:`\\psi(0)=\\psi_0` (bottom) and :math:`\\psi(L)=\\psi_L` (top) is\n\n.. math::\n\n \\psi(y) = \\frac{1}{\\alpha}\\,\\ln\\!\\Bigl[\n \\bigl(u_0 - q^*\\bigr)\\,e^{-\\alpha y} + q^*\n \\Bigr]\n\nwhere :math:`u_0 = e^{\\alpha\\psi_0}`,\n:math:`u_L = e^{\\alpha\\psi_L}`, and\n\n.. math::\n\n q^* \\equiv \\frac{q}{K_s}\n = \\frac{u_L - u_0\\,e^{-\\alpha L}}{1 - e^{-\\alpha L}}\n\nParameters\n----------\ny : float or array\n Vertical coordinate (0 = bottom, *L* = top).\npsi_0 : float\n Pressure head at the bottom boundary.\npsi_L : float\n Pressure head at the top boundary.\nL : float\n Column height.\nalpha : float\n Gardner sorptive number [1/length].\n\nReturns\n-------\nfloat or array\n Exact pressure head profile :math:`\\psi(y)`.\n\nNotes\n-----\nThis is a *numpy* function (not sympy) intended for comparing\nnumerical solutions against the analytical benchmark.", + "harvested_comments": [ + "Normalised steady-state flux q* = q / Ks" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "gardner_transient_psi", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 418, + "signature": "(y, t, psi_dry, psi_wet, L, Ks, alpha, theta_r, theta_s)", + "parameters": [ + { + "name": "y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "t", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "psi_dry", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "psi_wet", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "L", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Ks", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Analytical transient wetting-front solution for Gardner model.\n\nApplies the **Ogata\u2013Banks** (1961) solution to Richards equation\nwith Gardner conductivity in a vertical column of height *L*.\n\nThe substitution :math:`u = \\exp(\\alpha\\psi)` transforms the\nnonlinear Richards equation into linear advection\u2013diffusion:\n\n.. math::\n\n \\frac{\\partial u}{\\partial t}\n = D\\,\\frac{\\partial^2 u}{\\partial z^2}\n + V\\,\\frac{\\partial u}{\\partial z}\n\nwhere :math:`z = L - y` (depth from the top),\n:math:`D = K_s / (\\alpha\\,\\Delta\\theta)`,\n:math:`V = K_s / \\Delta\\theta`, and\n:math:`\\Delta\\theta = \\theta_s - \\theta_r`.\n\nWith a **step change** at the top (:math:`z = 0`) from dry to wet\nand a semi-infinite column approximation, the Ogata\u2013Banks solution\ngives\n\n.. math::\n\n u(z, t) = u_{\\rm dry}\n + (u_{\\rm wet} - u_{\\rm dry})\\,H(z, t)\n\nwhere\n\n.. math::\n\n H(z, t) = \\tfrac{1}{2}\\,\\operatorname{erfc}\\!\\left(\n \\frac{z - Vt}{2\\sqrt{Dt}}\\right)\n + \\tfrac{1}{2}\\,\\exp\\!\\left(\\frac{Vz}{D}\\right)\\,\n \\operatorname{erfc}\\!\\left(\\frac{z + Vt}{2\\sqrt{Dt}}\\right)\n\nFinally, :math:`\\psi(y, t) = \\ln(u) / \\alpha`.\n\nParameters\n----------\ny : float or array\n Vertical coordinate (0 = bottom, *L* = top).\nt : float\n Time since the wet boundary was applied (must be > 0).\npsi_dry : float\n Initial (dry) pressure head throughout the column.\npsi_wet : float\n Pressure head imposed at the top boundary.\nL : float\n Column height.\nKs : float\n Saturated hydraulic conductivity.\nalpha : float\n Gardner sorptive number [1/length].\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\n\nReturns\n-------\nfloat or array\n Pressure head profile :math:`\\psi(y, t)`.\n\nNotes\n-----\nThis is a *numpy* function (not sympy) intended for comparing\nnumerical solutions against the analytical benchmark.\n\nThe semi-infinite approximation is excellent when the wetting\nfront has not yet reached the bottom boundary.\n\nReferences\n----------\nOgata, A. and Banks, R. B. (1961). A solution of the differential\nequation of longitudinal dispersion in porous media.\n*US Geological Survey Professional Paper* 411-A.", + "harvested_comments": [ + "Depth from the top (z = 0 at top, z = L at bottom)", + "Ogata-Banks solution" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "haverkamp_theta", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 529, + "signature": "(psi, theta_r, theta_s, alpha, beta)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "beta", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Volumetric water content (Haverkamp model).\n\n.. math::\n\n \\theta(\\psi) = \\begin{cases}\n \\theta_r + \\dfrac{\\alpha\\,(\\theta_s - \\theta_r)}\n {\\alpha + |\\psi|^{\\beta}}\n & \\psi < 0 \\\\[6pt]\n \\theta_s & \\psi \\ge 0\n \\end{cases}\n\nUnlike Van Genuchten, the retention and conductivity curves have\n**independent** parameters, which gives extra flexibility when\nfitting laboratory data.\n\nParameters\n----------\npsi : sympy expression\n Pressure head (negative in unsaturated zone).\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\nalpha : float\n Retention shape parameter (dimensionless or [length]^beta,\n depending on convention).\nbeta : float\n Retention exponent.\n\nReturns\n-------\nsympy.Piecewise\n Water content expression.\n\nReferences\n----------\nHaverkamp, R. et al. (1977). A comparison of numerical simulation\nmodels for one-dimensional infiltration.\n*Soil Science Society of America Journal*, 41(2), 285--294.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "haverkamp_K", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 579, + "signature": "(psi, Ks, A, B)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Ks", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "A", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "B", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Hydraulic conductivity (Haverkamp model).\n\n.. math::\n\n K(\\psi) = \\begin{cases}\n K_s\\,\\dfrac{A}{A + |\\psi|^B} & \\psi < 0 \\\\[6pt]\n K_s & \\psi \\ge 0\n \\end{cases}\n\nThe conductivity parameters *A* and *B* are independent of the\nretention parameters *alpha* and *beta*.\n\nParameters\n----------\npsi : sympy expression\n Pressure head (negative in unsaturated zone).\nKs : float\n Saturated hydraulic conductivity.\nA : float\n Conductivity shape parameter.\nB : float\n Conductivity exponent.\n\nReturns\n-------\nsympy.Piecewise\n Hydraulic conductivity expression.\n\nReferences\n----------\nHaverkamp, R. et al. (1977). A comparison of numerical simulation\nmodels for one-dimensional infiltration.\n*Soil Science Society of America Journal*, 41(2), 285--294.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "haverkamp_C", + "kind": "function", + "file": "src/underworld3/utilities/retention_curves.py", + "line": 622, + "signature": "(psi, theta_r, theta_s, alpha, beta, Ss = 0.0)", + "parameters": [ + { + "name": "psi", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_r", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "theta_s", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "beta", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Ss", + "type_hint": null, + "default": "0.0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Specific moisture capacity (Haverkamp model).\n\n.. math::\n\n C(\\psi) = \\frac{d\\theta}{d\\psi} = \\begin{cases}\n \\dfrac{\\alpha\\,\\beta\\,(\\theta_s - \\theta_r)\\,|\\psi|^{\\beta - 1}}\n {\\bigl(\\alpha + |\\psi|^{\\beta}\\bigr)^2}\n & \\psi < 0 \\\\[6pt]\n S_s & \\psi \\ge 0\n \\end{cases}\n\nParameters\n----------\npsi : sympy expression\n Pressure head.\ntheta_r : float\n Residual water content.\ntheta_s : float\n Saturated water content.\nalpha : float\n Retention shape parameter.\nbeta : float\n Retention exponent.\nSs : float, optional\n Specific storage for the saturated zone (default 0).\n\nReturns\n-------\nsympy.Piecewise\n Specific moisture capacity expression.\n\nReferences\n----------\nHaverkamp, R. et al. (1977). A comparison of numerical simulation\nmodels for one-dimensional infiltration.\n*Soil Science Society of America Journal*, 41(2), 285--294.", + "harvested_comments": [ + "psi < 0, so |psi| = -psi" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "build_rotation", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 176, + "signature": "(solver, boundaries)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundaries", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Global sparse rotation Q on the composite saddle vector: identity except a\nper-node (normal,tangential) block at each velocity node of `boundaries`.\nReturns (Q, Qt, normal_rows) where normal_rows are the global rows carrying\nthe rotated NORMAL velocity component (v_n = n\u0302\u00b7v), to be strongly constrained.", + "harvested_comments": [ + "gather all normals per velocity node across the boundaries. Each entry of", + "`boundaries` is a name (geometric normal) or a (name, normal) pair.", + "Distributed Q with the assembled operator's ROW layout. Q is identity except a", + "per-node dim\u00d7dim orthonormal block; because a node's dim velocity components", + "live on a SINGLE DMPlex point owned by ONE rank, each block is entirely within" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "solve_rotated_freeslip", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 242, + "signature": "(solver, boundaries, remove_rotation_gauge = True, verbose = False)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundaries", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "remove_rotation_gauge", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Assemble + solve the rotated strong-free-slip Stokes saddle. Fills the\nsolver's velocity/pressure fields with the (rotated-back, gauge-removed)\nsolution. Returns a dict with the rotation Q and reaction data for \u03c3_nn.\n\nCalled from ``SNES_Stokes_SaddlePt.solve`` after ``_build`` (so the SNES/DM\nexist); when used standalone it builds the solver itself.", + "harvested_comments": [ + "Assemble the operator FIRST so its parallel row layout is final before we build", + "Q against it (A = exact Jacobian at 0 \u2014 linear; b = -F(0)). The Pmat is", + "assembled alongside: its p-p block is the native 1/mu pressure mass (the", + "DS JacobianPreconditioner term) that preconditions the Schur complement \u2014", + "petsc4py's computeJacobian(x, J) would silently pass J as its own Pmat and" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "solve_rotated_freeslip_nonlinear", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 415, + "signature": "(solver, boundaries, remove_rotation_gauge = True, verbose = False, zero_init_guess = True, picard = 0, rtol = None, atol = 1e-11, stol = 1e-08, max_it = 50)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundaries", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "remove_rotation_gauge", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "zero_init_guess", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "picard", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "rtol", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "atol", + "type_hint": null, + "default": "1e-11", + "description": "" + }, + { + "name": "stol", + "type_hint": null, + "default": "1e-08", + "description": "" + }, + { + "name": "max_it", + "type_hint": null, + "default": "50", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Nonlinear rotated strong-free-slip solve: a manual outer Newton/Picard loop\nthat rotates the residual F(u), the Jacobian J(u) and the v_n=0 constraint EVERY\niteration, reusing the validated self-contained rotated fieldsplit-Schur solve\n(``_solve_rotated_iterative``, incl. custom geometric FMG / GAMG velocity block\nand the rotated coupled null space) for each Newton increment.\n\nWhy a manual loop rather than ``snes.solve()``: the rotated operator ``Q A Q\u1d40``\n(a ``ptap`` result) carries no DM field information, so PETSc's DM-coupled\nfieldsplit + geometric-MG cannot precondition it (SUBPC_ERROR); the increment\nmust be solved by the IS-based self-contained fieldsplit. Driving that from a\nmanual Newton loop keeps the whole validated linear machinery and imposes the\nstrong constraint exactly at every iterate.\n\nEach iteration (unknown carried in the CARTESIAN frame ``u``; the increment is\nsolved in the rotated frame ``\u00fb = Q u``):\n * ``F = computeFunction(u)`` \u2192 Cartesian residual (native essential BCs on\n other boundaries already applied by the DM);\n * ``F\u0302 = Q F``, zero ``F\u0302`` at the constrained normal rows (the constraint\n residual is 0 there); converge on \u2016F\u0302\u2016;\n * ``\u0134 = Q J(u) Q\u1d40`` with ``zeroRowsColumns(normal_rows)``;\n * solve ``\u0134 \u03b4\u0302 = \u2212F\u0302``, ``\u03b4 = Q\u1d40 \u03b4\u0302``, with a \u2016F\u0302\u2016 backtracking line search;\n * ``u += \u03b1 \u03b4`` and re-impose ``v_n = 0`` exactly.\n\nThe tangent used by ``computeJacobian`` is the solver's own (``consistent_jacobian``\n\u2192 Picard / Newton / continuation), so the rotated loop inherits the same tangent\nthe standard path would use. The converged Cartesian residual is stashed as the\nconstraint reaction for \u03c3_nn recovery (``boundary_normal_traction``).\n\nTangent / warmup handling (mirrors the standard ``solve`` path so nothing is\nsilently ignored):\n\n* ``consistent_jacobian == \"continuation\"`` \u2014 a two-phase Picard\u2192Newton solve via\n the ``constants[]``-routed blend \u03b1: phase 1 holds \u03b1=0 (frozen/Picard tangent) to\n the loose ``solver.newton_switch_rtol`` (and at least ``picard`` iterations if\n given) to enter Newton's basin; phase 2 sets \u03b1=1 (consistent Newton tangent) and\n drives to the requested tolerance. \u03b1 is restored to 0 afterwards.\n* ``consistent_jacobian is True`` (pure Newton) with ``picard > 0`` \u2014 a Picard\n warmup needs the frozen tangent, which the pure-Newton compile does not carry, so\n this **raises** ``NotImplementedError`` pointing to ``\"continuation\"`` rather than\n silently ignoring the request.\n* ``consistent_jacobian is False`` (default, frozen/Picard) \u2014 the whole solve is\n already the frozen tangent, so ``picard`` is inherently satisfied (matches the\n standard path, whose post-warmup Newton phase also uses the frozen tangent here).", + "harvested_comments": [ + "Resolve the tangent / warmup policy (see the docstring). ``continuation`` runs a", + "staged \u03b1=0 \u2192 \u03b1=1 solve; pure Newton + picard>0 is unsupported (no frozen tangent", + "to warm up with) and errors loudly; frozen (default) already satisfies a warmup.", + "start in the Picard phase", + "Q, the custom-FMG prolongation and the coupled null space depend only on the" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "boundary_normal_traction", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 917, + "signature": "(solver, boundary, info, mass = 'lumped')", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "info", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mass", + "type_hint": null, + "default": "'lumped'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Boundary normal traction \u03c3_nn on `boundary` from the constraint reaction of the\nlast rotated-free-slip solve. Returned mean-removed (the \u03c1g\u00b7h gauge), as\n``(xs, sigma)`` with one entry per boundary velocity node on this rank.\n\n\u03c3_nn is recovered from the CARTESIAN nodal reaction r_c = A\u00b7u \u2212 b: the nodal load\nis R_i = n\u0302_i \u00b7 r_c(node_i), where n\u0302_i is THIS boundary's outward normal at node i.\nProjecting the Cartesian reaction onto the boundary normal (rather than reading the\nrotated frame's normal row) is corner-correct \u2014 at a node shared with another\nrotated-free-slip boundary the rotated frame's first row is a mix of both walls'\nnormals, but n\u0302\u00b7r_c is the true normal traction for this boundary. The pointwise\n\u03c3_nn is the boundary-mass de-smear of R (2D).\n\n``mass`` selects the de-smear:\n * ``\"lumped\"`` (default) \u2014 the diagonal (row-sum) boundary mass. Being an M-matrix\n it CANNOT overshoot at a stress discontinuity (no Gibbs wiggle where the traction\n jumps, e.g. across a viscosity contrast), it is a purely local division (no global\n mass solve \u2192 trivially parallel), and it is marginally more accurate than the\n consistent mass on SolCx. Recommended for driving a free surface, where an\n overshoot at a sharp feature injects a spurious surface-velocity pulse.\n * ``\"consistent\"`` \u2014 the full consistent P2 line mass. Marginally sharper on smooth\n tractions but overshoots at discontinuities.\n\nParallel-safe: r_c is scattered to a local vector (ghosts included) and read by LOCAL\nsection offset; the boundary mass is assembled globally by a coordinate-keyed\nallgather of the boundary elements, so every rank produces the same de-smear and the\nmean-removal gauge is global.", + "harvested_comments": [ + "Cartesian nodal reaction r_c = F(u) at the converged state. The nonlinear", + "driver stashes it directly (the final ``computeFunction`` residual); the linear", + "one-shot reconstructs it as A\u00b7u\u2212b (with A=J(0), b=\u2212F(0), F affine \u21d2 A\u00b7u\u2212b=F(u)).", + "owned by info \u2014 do NOT destroy", + "Cartesian reaction at this node (local)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "dynamic_topography_field", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 982, + "signature": "(solver, boundary, info, field, buoyancy_scale = 1.0, mass = 'lumped')", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "info", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "buoyancy_scale", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "mass", + "type_hint": null, + "default": "'lumped'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Populate a scalar MeshVariable ``field`` with the dynamic topography\n:math:`h = -(\\sigma_{nn}-\\overline{\\sigma_{nn}})/(\\Delta\\rho\\,g)` on ``boundary``,\nrecovered from the rotated-free-slip constraint reaction (lumped by default \u2014\nmonotone, no Gibbs overshoot at a stress jump). Interior nodes are left untouched.\nReturns ``field``.\n\nThis is the hand-off to the free-surface machinery: the 3-number topography\nintegrator drives node motion from a surface field, so \u03c3_nn is written onto the\nboundary nodes of a P1 (or higher) scalar field it can read / BdIntegral. Parallel-\nsafe: the recovery is partition-independent and the write is local (each rank fills\nits own boundary nodes, matched by coordinate to the recovery output).", + "harvested_comments": [ + "\u03c3_nn is already mean-removed (the \u03c1g\u00b7h gauge); topography h = -\u03c3_nn / (\u0394\u03c1 g).", + "shared bulk field write (parallel-safe: write ONCE, not per node)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 155, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the units of this array.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "has_units", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 160, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if this array has units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "dimensionality", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 165, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the dimensionality of this array.", + "harvested_comments": [ + "Use Pint directly to get dimensionality" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "unit_checking", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 178, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if unit compatibility checking is enabled.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "unit_checking", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 183, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Enable/disable unit compatibility checking.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "auto_convert", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 188, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Check if automatic unit conversion is enabled.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "auto_convert", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 193, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Enable/disable automatic unit conversion.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "magnitude", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 198, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the numerical values without units (like Pint's .magnitude).\n\nThis returns a plain numpy array view of the data, stripping units.\nUseful when you need raw numerical values for dimensionless calculations.\n\nReturns\n-------\nnp.ndarray\n Plain numpy array without unit tracking\n\nExamples\n--------\n>>> coords = mesh.X.coords # UnitAwareArray with units=\"km\"\n>>> x, y = coords[:, 0].magnitude, coords[:, 1].magnitude # Plain arrays\n>>> temperature.array[:, 0, 0] = 300 + 2.6 * y # Works - no units", + "harvested_comments": [ + "UnitAwareArray with units=\"km\"", + "Plain arrays", + "Works - no units", + "Use numpy's asarray to get a plain numpy array", + "This avoids our overridden view() method which preserves units" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "to", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 220, + "signature": "(self, target_units)", + "parameters": [ + { + "name": "target_units", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert this array to different units.\n\nProvides a unified interface matching Pint's `.to()` pattern.\n\nParameters\n----------\ntarget_units : str\n Target units to convert to\n\nReturns\n-------\nUnitAwareArray\n New array with converted values and target units\n\nExamples\n--------\n>>> coords = UnitAwareArray([1, 2, 3], units='km')\n>>> coords_m = coords.to('m') # Convert to meters\n>>> print(coords_m)\n[1000. 2000. 3000.] [meter]", + "harvested_comments": [ + "Convert to meters", + "Use Pint directly for more reliable conversion", + "Get the unit registry from UW3's scaling module", + "Create Pint quantities using the registry", + "Create new UnitAwareArray with converted values" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "max", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 497, + "signature": "(self, axis = None, out = None, keepdims = False, initial = None, where = True)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "initial", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "where", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return maximum with units preserved.", + "harvested_comments": [ + "Scalar result - wrap with units", + "Array result - wrap as UnitAwareArray" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "min", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 508, + "signature": "(self, axis = None, out = None, keepdims = False, initial = None, where = True)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "initial", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "where", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return minimum with units preserved.", + "harvested_comments": [ + "Scalar result - wrap with units", + "Array result - wrap as UnitAwareArray" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "mean", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 519, + "signature": "(self, axis = None, dtype = None, out = None, keepdims = False, where = True)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "where", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return mean with units preserved.", + "harvested_comments": [ + "Scalar result - wrap with units", + "Array result - wrap as UnitAwareArray" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "sum", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 530, + "signature": "(self, axis = None, dtype = None, out = None, keepdims = False, initial = None, where = True)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "initial", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "where", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return sum with units preserved.", + "harvested_comments": [ + "Scalar result - wrap with units", + "Array result - wrap as UnitAwareArray" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "std", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 543, + "signature": "(self, axis = None, dtype = None, out = None, ddof = 0, keepdims = False, where = True)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "ddof", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "where", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return standard deviation with units preserved.", + "harvested_comments": [ + "No units - use numpy's default", + "Calculate std using unit-aware variance (avoid numpy's internal mean)", + "Take square root of variance", + "Scalar result - extract magnitude, compute sqrt, re-wrap with units", + "Array result" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "var", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 574, + "signature": "(self, axis = None, dtype = None, out = None, ddof = 0, keepdims = False, where = True)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "ddof", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "where", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return variance with units squared.", + "harvested_comments": [ + "No units - use numpy's default", + "Calculate variance manually using unit-aware mean to avoid numpy's internal mean", + "var = mean((x - mean(x))**2)", + "Get unit-aware mean", + "Compute deviations: (x - mean)" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_max", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 638, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return maximum across all MPI ranks with units preserved.\n\nFor scalar results (axis=None), performs MPI reduction. For array results,\nperforms component-wise maximum. For tensors (ndim > 2), raises NotImplementedError.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nUWQuantity or ndarray\n Global maximum with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Check dimensionality for tensor rejection", + "Delegate MPI reduction to parent class", + "Wrap result with units if present" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_min", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 684, + "signature": "(self, axis = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return minimum across all MPI ranks with units preserved.\n\nFor scalar results (axis=None), performs MPI reduction. For array results,\nperforms component-wise minimum. For tensors (ndim > 2), raises NotImplementedError.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nUWQuantity or ndarray\n Global minimum with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Check dimensionality for tensor rejection", + "Delegate MPI reduction to parent class", + "Wrap result with units if present" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_sum", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 730, + "signature": "(self, axis = None, dtype = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return sum across all MPI ranks with units preserved.\n\nFor scalar results (axis=None), performs MPI reduction. For array results,\nperforms component-wise sum. For tensors (ndim > 2), raises NotImplementedError.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\ndtype : data-type, optional\n Type of returned array\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nUWQuantity or ndarray\n Global sum with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Check dimensionality for tensor rejection", + "Delegate MPI reduction to parent class", + "Wrap result with units if present" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_mean", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 778, + "signature": "(self, axis = None, dtype = None, out = None, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "out", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return mean across all MPI ranks with units preserved.\n\nComputes the true global mean by summing all values across ranks and\ndividing by total count. For tensors (ndim > 2), raises NotImplementedError.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\ndtype : data-type, optional\n Type of returned array\nout : ndarray, optional\n Alternative output array\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nUWQuantity or ndarray\n Global mean with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Check dimensionality for tensor rejection", + "Delegate MPI reduction to parent class", + "Wrap result with units if present" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_var", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 826, + "signature": "(self, axis = None, dtype = None, ddof = 0, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "ddof", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return variance across all MPI ranks with units squared preserved.\n\nUses parallel variance algorithm (Welford/Chan) for numerical stability.\nFor tensors (ndim > 2), raises NotImplementedError.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\ndtype : data-type, optional\n Type of returned array\nddof : int, optional\n Delta degrees of freedom (default: 0)\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nUWQuantity or ndarray\n Global variance with units squared\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Check dimensionality for tensor rejection", + "Get local statistics", + "Extract magnitude for calculations", + "Compute local sum of squared deviations", + "Global reduce" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_std", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 944, + "signature": "(self, axis = None, dtype = None, ddof = 0, keepdims = False)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "ddof", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "keepdims", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return standard deviation across all MPI ranks with units preserved.\n\nComputed as square root of global variance. For tensors (ndim > 2),\nraises NotImplementedError.\n\nParameters\n----------\naxis : None or int or tuple of ints, optional\n Axis along which to operate (default: None = reduce all dimensions)\ndtype : data-type, optional\n Type of returned array\nddof : int, optional\n Delta degrees of freedom (default: 0)\nkeepdims : bool, optional\n Keep reduced dimensions as size 1 (default: False)\n\nReturns\n-------\nUWQuantity or ndarray\n Global standard deviation with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Get global variance", + "Take square root", + "Scalar result", + "Array result" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_norm", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1001, + "signature": "(self, ord = None)", + "parameters": [ + { + "name": "ord", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return norm across all MPI ranks.\n\nFor scalars (ndim=1), computes sqrt(sum of squares). For vectors,\ncomputes vector norm. For tensors (ndim > 2), raises NotImplementedError.\n\nParameters\n----------\nord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional\n Order of the norm (default: None = 2-norm)\n\nReturns\n-------\nUWQuantity or float\n Global norm with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)", + "harvested_comments": [ + "Check dimensionality for tensor rejection", + "Delegate MPI reduction to parent class", + "Wrap result with units if present" + ], + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_size", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1039, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return total number of elements across all MPI ranks.\n\nUseful for computing global statistics that require total element count,\nsuch as RMS or normalized quantities.\n\nReturns\n-------\nint\n Total number of elements summed across all MPI ranks\n\nExamples\n--------\n>>> coords = mesh.X.coords # Shape: (N_local, 2)\n>>> total_points = coords.global_size() # Sum of N_local across all ranks\n>>> rms = coords.global_norm() / np.sqrt(total_points)", + "harvested_comments": [ + "Shape: (N_local, 2)", + "Sum of N_local across all ranks", + "Delegate to parent class (no unit handling needed for size)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "global_rms", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1060, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return root mean square across all MPI ranks with units preserved.\n\nComputes RMS = sqrt(sum of squares / total count) across all ranks.\nFor tensors (ndim > 2), raises NotImplementedError.\n\nThe RMS is computed as:\nRMS = global_norm() / sqrt(global_size())\n\nReturns\n-------\nUWQuantity or float\n Global RMS with units preserved\n\nRaises\n------\nNotImplementedError\n If called on tensor data (ndim > 2)\n\nExamples\n--------\n>>> coords = mesh.X.coords # UnitAwareArray with units=\"km\"\n>>> rms_coord = coords.global_rms() # Returns UWQuantity in km\n>>> print(f\"RMS coordinate: {rms_coord}\")", + "harvested_comments": [ + "UnitAwareArray with units=\"km\"", + "Returns UWQuantity in km", + "Check dimensionality for tensor rejection", + "Delegate MPI reduction to parent class", + "Wrap result with units if present" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "copy", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1268, + "signature": "(self, order = 'C')", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "'C'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a copy of the array with preserved units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "astype", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1278, + "signature": "(self, dtype, order = 'K', casting = 'unsafe', subok = True, copy = True)", + "parameters": [ + { + "name": "dtype", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "order", + "type_hint": null, + "default": "'K'", + "description": "" + }, + { + "name": "casting", + "type_hint": null, + "default": "'unsafe'", + "description": "" + }, + { + "name": "subok", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "copy", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert array type while preserving units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "view", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1292, + "signature": "(self, dtype = None, type = None)", + "parameters": [ + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "type", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a view of the array with preserved units.", + "harvested_comments": [ + "Units should already be copied via __array_finalize__", + "Create UnitAwareArray view" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "reshape", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1311, + "signature": "(self, *shape)", + "parameters": [ + { + "name": "*shape", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a reshaped array with preserved units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "flatten", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1321, + "signature": "(self, order = 'C')", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": "'C'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a flattened array with preserved units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "squeeze", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1331, + "signature": "(self, axis = None)", + "parameters": [ + { + "name": "axis", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a squeezed array with preserved units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "transpose", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1341, + "signature": "(self, *axes)", + "parameters": [ + { + "name": "*axes", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Return a transposed array with preserved units.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareArray", + "is_public": true + }, + { + "name": "create_unit_aware_array", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1608, + "signature": "(data, units = None, **kwargs)", + "parameters": [ + { + "name": "data", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convenience function to create a UnitAwareArray.\n\nParameters\n----------\ndata : array-like\n Input data\nunits : str, optional\n Units for the array\n**kwargs : dict\n Additional arguments passed to UnitAwareArray\n\nReturns\n-------\nUnitAwareArray\n Array with unit tracking", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "zeros_with_units", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1629, + "signature": "(shape, units, dtype = float, **kwargs)", + "parameters": [ + { + "name": "shape", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "float", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a zero-filled UnitAwareArray with specified units.\n\nParameters\n----------\nshape : tuple\n Shape of the array\nunits : str\n Units for the array\ndtype : data-type, optional\n Data type of the array\n**kwargs : dict\n Additional arguments passed to UnitAwareArray\n\nReturns\n-------\nUnitAwareArray\n Zero-filled array with units", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "ones_with_units", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1653, + "signature": "(shape, units, dtype = float, **kwargs)", + "parameters": [ + { + "name": "shape", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "float", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a ones-filled UnitAwareArray with specified units.\n\nParameters\n----------\nshape : tuple\n Shape of the array\nunits : str\n Units for the array\ndtype : data-type, optional\n Data type of the array\n**kwargs : dict\n Additional arguments passed to UnitAwareArray\n\nReturns\n-------\nUnitAwareArray\n Ones-filled array with units", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "full_with_units", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1677, + "signature": "(shape, fill_value, units, dtype = None, **kwargs)", + "parameters": [ + { + "name": "shape", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fill_value", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dtype", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a UnitAwareArray filled with a specified value and units.\n\nParameters\n----------\nshape : tuple\n Shape of the array\nfill_value : scalar\n Fill value for the array\nunits : str\n Units for the array\ndtype : data-type, optional\n Data type of the array\n**kwargs : dict\n Additional arguments passed to UnitAwareArray\n\nReturns\n-------\nUnitAwareArray\n Filled array with units", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "test_unit_aware_array", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1704, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Test the UnitAwareArray functionality.", + "harvested_comments": [ + "Basic creation", + "Unit-preserving operations", + "Unit compatibility checking", + "Should fail", + "Same units addition" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "units", + "kind": "property", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 54, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get the units of this coordinate.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareBaseScalar", + "is_public": true + }, + { + "name": "units", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 59, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Set the units of this coordinate.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareBaseScalar", + "is_public": true + }, + { + "name": "get_units", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 76, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Get units for compatibility with get_units() function.\n\nReturns:\n Units of this coordinate or None if dimensionless", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareBaseScalar", + "is_public": true + }, + { + "name": "create_unit_aware_coordinate_system", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 117, + "signature": "(name, units = None)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a coordinate system with unit-aware coordinates.\n\nThis function creates a SymPy CoordSys3D-like object but with\nUnitAwareBaseScalar coordinates that carry units information.\n\nParameters:\n name: Name of the coordinate system (e.g., \"N\")\n units: Units for the coordinates (e.g., \"meter\", \"kilometer\")\n\nReturns:\n A coordinate system object with unit-aware x, y, z coordinates", + "harvested_comments": [ + "Create a standard coordinate system first", + "Replace the standard BaseScalar coordinates with unit-aware ones", + "Note: We need to maintain the same structure for JIT compatibility", + "Store original coordinates", + "Create unit-aware replacements" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "patch_coordinate_units", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 178, + "signature": "(mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Patch existing mesh coordinates to be unit-aware.\n\nThis function takes an existing mesh with standard BaseScalar coordinates\nand adds unit awareness to them. This is done by monkey-patching the\nunits property onto the existing coordinate objects.\n\nParameters:\n mesh: The mesh whose coordinates should be made unit-aware", + "harvested_comments": [ + "Get mesh units if available", + "If mesh doesn't have explicit units but we have a model with reference scales,", + "use the length units from the model", + "Store Pint Unit objects, NOT strings", + "Already a Pint Unit" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "get_coordinate_units", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 276, + "signature": "(coord)", + "parameters": [ + { + "name": "coord", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Get units from a coordinate symbol.\n\nThis function works with both standard BaseScalar (returns None)\nand UnitAwareBaseScalar (returns units).\n\nParameters:\n coord: A coordinate symbol (BaseScalar or UnitAwareBaseScalar)\n\nReturns:\n Units of the coordinate or None if not unit-aware", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "swarm_h5", + "kind": "function", + "file": "src/underworld3/utilities/uw_swarmIO.py", + "line": 13, + "signature": "(swarm, fileName, timestep, fields = None, outputPath = '', compression = False, compressionType = 'gzip')", + "parameters": [ + { + "name": "swarm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fileName", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "timestep", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fields", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "outputPath", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "compression", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "compressionType", + "type_hint": null, + "default": "'gzip'", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Function to save swarm fields to h5 file for checkpointing purposes.\n\nswarm : The swarm that contains the coordinate data\nfileName : Name of file the file where the swarm data is stored.\nfields : UW swarm variables that contain the data to save. Should be passed as a list.\ntimestep : timestep of the model to save\noutputPath : Folder to save the data, can be left undefined to save in the current working directory\ncompression : Whether to compress the h5 files [bool]\ncompressionType : The type of compression to use. 'gzip' and 'lzf' are the supported types, with 'gzip' as the default.\n\n>>>\nexample usage:\nwithout compression:\nswarm_h5(swarm=swarm, fileName='swarm', fields=[material, strainRate], timestep=0)\n\nwith compression:\nswarm_h5(swarm=swarm, fileName='swarm', fields=[material, strainRate], timestep=0, compression=True, compressionType='gzip')\n\n<<<<", + "harvested_comments": [ + "## save the swarm particle location", + "### Generate a h5 file for each field" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "swarm_xdmf", + "kind": "function", + "file": "src/underworld3/utilities/uw_swarmIO.py", + "line": 60, + "signature": "(fileName, timestep, fields = None, outputPath = '', time = None)", + "parameters": [ + { + "name": "fileName", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "timestep", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fields", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "outputPath", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "time", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Function to combine h5 files to a single xdmf to visualise the swarm in paraview. Should be run after the 'swarm_h5' function.\n\nfileName : Name of file the file where the swarm data is stored.\ntimestep : timestep of the model to save\nfields : UW swarm variables that contain the data to save. Should be passed as a list.\noutputPath : Folder to save the data, can be left undefined to save in the current working directory. This directory should also contain the h5 files\ntime : Time of the model, can be left undefined and model output will be sequential but not contain the time data.\n\n>>>\nexample usage:\n\nswarm_xdmf(fileName='swarm', fields=[material, strainRate], timestep=0)\n\n<<<<", + "harvested_comments": [ + "Write the XDMF header", + "Write the grid element for the HDF5 dataset", + "Write the attribute element for the field", + "Write the XDMF footer" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "parallel_line_plot", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 13, + "signature": "(field, sample_points, title = 'Field Profile', xlabel = 'Position', ylabel = 'Field Value', viz_rank = 0)", + "parameters": [ + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sample_points", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "title", + "type_hint": null, + "default": "'Field Profile'", + "description": "" + }, + { + "name": "xlabel", + "type_hint": null, + "default": "'Position'", + "description": "" + }, + { + "name": "ylabel", + "type_hint": null, + "default": "'Field Value'", + "description": "" + }, + { + "name": "viz_rank", + "type_hint": null, + "default": "0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create a 1D line plot in parallel notebooks.\n\nArgs:\n field: UW field/expression to plot\n sample_points: numpy array of coordinates to sample\n title: Plot title\n xlabel, ylabel: Axis labels\n viz_rank: Which rank creates the visualization (default: 0)", + "harvested_comments": [ + "Designated rank requests data and creates plot", + "Other ranks participate with empty requests" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "parallel_scatter_plot", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 45, + "signature": "(field_x, field_y, sample_points, labels = ('Field X', 'Field Y'), title = 'Scatter Plot', viz_rank = 0)", + "parameters": [ + { + "name": "field_x", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sample_points", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "labels", + "type_hint": null, + "default": "('Field X', 'Field Y')", + "description": "" + }, + { + "name": "title", + "type_hint": null, + "default": "'Scatter Plot'", + "description": "" + }, + { + "name": "viz_rank", + "type_hint": null, + "default": "0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create scatter plot of two fields at sample points.\n\nArgs:\n field_x, field_y: UW fields/expressions to plot\n sample_points: numpy array of coordinates to sample\n labels: Tuple of (xlabel, ylabel)\n title: Plot title\n viz_rank: Which rank creates the visualization", + "harvested_comments": [ + "Gather both fields", + "Other ranks participate" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "parallel_profile_comparison", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 79, + "signature": "(fields, sample_points, labels = None, title = 'Field Profiles', viz_rank = 0)", + "parameters": [ + { + "name": "fields", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sample_points", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "labels", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "title", + "type_hint": null, + "default": "'Field Profiles'", + "description": "" + }, + { + "name": "viz_rank", + "type_hint": null, + "default": "0", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Plot multiple field profiles on the same axes for comparison.\n\nArgs:\n fields: List of UW fields/expressions\n sample_points: numpy array of coordinates to sample\n labels: List of field names (optional)\n title: Plot title\n viz_rank: Which rank creates the visualization", + "harvested_comments": [ + "Gather all field data", + "Other ranks participate" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "parallel_custom_plot", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 121, + "signature": "(plot_function, field_data_specs, viz_rank = 0, **plot_kwargs)", + "parameters": [ + { + "name": "plot_function", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_data_specs", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "viz_rank", + "type_hint": null, + "default": "0", + "description": "" + }, + { + "name": "**plot_kwargs", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Generic helper for parallel plotting with custom plot functions.\n\nArgs:\n plot_function: Function that creates the plot, receives gathered data as dict\n field_data_specs: List of (field, points, name) tuples\n viz_rank: Which rank creates the visualization\n **plot_kwargs: Additional keyword arguments passed to plot_function\n\nExample:\n def my_custom_plot(velocity_data, velocity_points, temperature_data, temperature_points):\n import matplotlib.pyplot as plt\n fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))\n ax1.plot(velocity_points[:, 1], velocity_data.flatten())\n ax2.plot(temperature_points[:, 0], temperature_data.flatten())\n plt.show()\n\n parallel_custom_plot(\n my_custom_plot,\n [(velocity[1], y_line, \"velocity\"),\n (temperature, x_line, \"temperature\")]\n )", + "harvested_comments": [ + "Gather all field data", + "Create visualization with gathered data", + "Other ranks participate in data gathering" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_line_sample", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 164, + "signature": "(start, end, num_points = 50)", + "parameters": [ + { + "name": "start", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "end", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "num_points", + "type_hint": null, + "default": "50", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create evenly spaced points along a line.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_vertical_line", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 171, + "signature": "(x, y_range = (0, 1), num_points = 50)", + "parameters": [ + { + "name": "x", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "y_range", + "type_hint": null, + "default": "(0, 1)", + "description": "" + }, + { + "name": "num_points", + "type_hint": null, + "default": "50", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create vertical line at fixed x coordinate.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_horizontal_line", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 177, + "signature": "(y, x_range = (0, 1), num_points = 50)", + "parameters": [ + { + "name": "y", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "x_range", + "type_hint": null, + "default": "(0, 1)", + "description": "" + }, + { + "name": "num_points", + "type_hint": null, + "default": "50", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create horizontal line at fixed y coordinate.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "create_diagonal_sample", + "kind": "function", + "file": "src/underworld3/visualisation/parallel.py", + "line": 183, + "signature": "(domain_bounds = ((0, 1), (0, 1)), num_points = 50)", + "parameters": [ + { + "name": "domain_bounds", + "type_hint": null, + "default": "((0, 1), (0, 1))", + "description": "" + }, + { + "name": "num_points", + "type_hint": null, + "default": "50", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create diagonal line sampling across domain.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "initialise", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 10, + "signature": "(jupyter_backend)", + "parameters": [ + { + "name": "jupyter_backend", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Initialize PyVista with Underworld-friendly defaults.\n\nSets up PyVista global theme with white background, anti-aliasing,\nand appropriate Jupyter backend.\n\nParameters\n----------\njupyter_backend : str or None\n Jupyter backend to use ('trame', 'client', 'panel', etc.).\n If None, auto-detects based on environment.", + "harvested_comments": [ + "Check if we're in a remote Jupyter environment (binder, JupyterHub, etc.)", + "Use trame backend for all Jupyter environments", + "Configure trame for remote environments (binder, JupyterHub)", + "Enable server proxy but let PyVista auto-detect the prefix", + "Don't set prefix - let PyVista/trame auto-detect from environment" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": true + }, + { + "name": "mesh_to_pv_mesh", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 56, + "signature": "(mesh, jupyter_backend = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "jupyter_backend", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert Underworld mesh to PyVista unstructured grid.\n\nParameters\n----------\nmesh : Mesh\n Underworld mesh to convert.\njupyter_backend : str, optional\n PyVista Jupyter backend to use.\n\nReturns\n-------\npyvista.UnstructuredGrid\n PyVista mesh with unit metadata attached.", + "harvested_comments": [ + "# Required in notebooks", + "import nest_asyncio", + "nest_asyncio.apply()", + "import shutil", + "import tempfile" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "coords_to_pv_coords", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 190, + "signature": "(coords)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert coordinate array to PyVista-compatible 3D coordinates.\n\nParameters\n----------\ncoords : numpy.ndarray\n Coordinate array of shape ``(n, 2)`` or ``(n, 3)``.\n\nReturns\n-------\nnumpy.ndarray\n 3D coordinate array of shape ``(n, 3)``.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "swarm_to_pv_cloud", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 220, + "signature": "(swarm)", + "parameters": [ + { + "name": "swarm", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert swarm particle positions to PyVista point cloud.\n\nParameters\n----------\nswarm : Swarm\n Underworld swarm object.\n\nReturns\n-------\npyvista.PolyData\n Point cloud with particle positions.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "meshVariable_to_pv_cloud", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 249, + "signature": "(meshVar)", + "parameters": [ + { + "name": "meshVar", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert mesh variable node positions to PyVista point cloud.\n\nParameters\n----------\nmeshVar : MeshVariable\n Underworld mesh variable.\n\nReturns\n-------\npyvista.PolyData\n Point cloud at mesh variable nodal locations.", + "harvested_comments": [ + "Get coordinates from the mesh variable", + "These may be dimensional (UnitAwareArray with meters) when units are active", + "The alpha parameter in meshVariable_to_pv_mesh_object is now computed from", + "these coordinates directly, ensuring scale consistency", + "Store units metadata for labeling (same as mesh_to_pv_mesh)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "meshVariable_to_pv_mesh_object", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 292, + "signature": "(meshVar, alpha = None)", + "parameters": [ + { + "name": "meshVar", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "alpha", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert mesh variable to Delaunay-triangulated PyVista mesh.\n\nCreates a mesh by triangulating the mesh variable's nodal points.\nUseful for higher-order elements where the base mesh doesn't\ncapture all data points.\n\nParameters\n----------\nmeshVar : MeshVariable\n Underworld mesh variable.\nalpha : float, optional\n Alpha parameter for Delaunay triangulation. If None, computed\n automatically from coordinate range.\n\nReturns\n-------\npyvista.UnstructuredGrid\n Triangulated mesh through the variable's nodal points.", + "harvested_comments": [ + "Compute alpha from the point cloud coordinates themselves", + "This ensures consistency between coordinate scale and alpha parameter", + "mesh.get_max_radius() returns nondimensional values but coords may be", + "dimensional when units are active - causing Delaunay to fail", + "Use a fraction of the coordinate range as a reasonable alpha" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "scalar_fn_to_pv_points", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 346, + "signature": "(pv_mesh, uw_fn, dim = None)", + "parameters": [ + { + "name": "pv_mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "uw_fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Evaluate Underworld scalar function at PyVista mesh points.\n\nParameters\n----------\npv_mesh : pyvista.DataSet\n PyVista mesh or point cloud to evaluate at.\nuw_fn : sympy.Expr\n Underworld scalar function to evaluate.\ndim : int, optional\n Dimensionality (2 or 3). Auto-detected if None.\n\nReturns\n-------\nnumpy.ndarray\n Scalar values at mesh points (units stripped for PyVista).\n The units string is stored as ``pv_mesh._last_scalar_units``.", + "harvested_comments": [ + "Use stored coordinate array if available (preserves units and dimensional info)", + "Capture units before stripping for colorbar labels", + "Strip units for PyVista compatibility" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "vector_fn_to_pv_points", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 399, + "signature": "(pv_mesh, uw_fn, dim = None)", + "parameters": [ + { + "name": "pv_mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "uw_fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Evaluate Underworld vector function at PyVista mesh points.\n\nParameters\n----------\npv_mesh : pyvista.DataSet\n PyVista mesh or point cloud to evaluate at.\nuw_fn : sympy.Matrix\n Underworld vector function to evaluate.\ndim : int, optional\n Dimensionality (not used, derived from function shape).\n\nReturns\n-------\nnumpy.ndarray\n Vector values at mesh points, shape ``(n_points, 3)``.\n Units string stored as ``pv_mesh._last_vector_units``.", + "harvested_comments": [ + "Capture units before stripping" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "clip_mesh", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 469, + "signature": "(pvmesh, clip_angle)", + "parameters": [ + { + "name": "pvmesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "clip_angle", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Clip the given mesh using planes at the specified angle.\n\nParameters:\n-----------\npvmesh : object\n The PyVista mesh object to be clipped.\n\nclip_angle : float\n The angle (in degrees) at which to clip the mesh.\n\nReturns:\n--------\nlist\n A list containing the two clipped mesh parts.", + "harvested_comments": [ + "Calculate normals for clipping planes", + "Perform clipping" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "plot_mesh", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 503, + "signature": "(mesh, title = '', clip_angle = 0.0, cpos = 'xy', window_size = (750, 750), show_edges = True, save_png = False, dir_fname = '')", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "title", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "clip_angle", + "type_hint": null, + "default": "0.0", + "description": "" + }, + { + "name": "cpos", + "type_hint": null, + "default": "'xy'", + "description": "" + }, + { + "name": "window_size", + "type_hint": null, + "default": "(750, 750)", + "description": "" + }, + { + "name": "show_edges", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "save_png", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "dir_fname", + "type_hint": null, + "default": "''", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Plot a mesh with optional clipping, edge display, and saving functionality.\n\nParameters:\n-----------\nmesh : object\n The mesh object to be plotted. This should be in a format that can be converted\n into a PyVista mesh using `vis.mesh_to_pv_mesh()`.\n\ntitle : str, optional\n The title text to be displayed on the plot. Default is an empty string, meaning no title is shown.\n\nclip_angle : float, optional\n The angle (in degrees) at which to clip the mesh. If set to 0.0, no clipping is applied.\n Clipping is performed using planes at the specified angle. Default is 0.0.\n\ncpos : str or list, optional\n The camera position for viewing the mesh. It can be a string such as 'xy', 'xz', 'yz', or\n a list specifying the exact camera position. Default is 'xy'.\n\nwindow_size : tuple of int, optional\n The size of the rendering window in pixels as (width, height). Default is (750, 750).\n\nshow_edges : bool, optional\n Whether to display the edges of the mesh in the plot. If `True`, edges will be shown.\n Default is `True`.\n\nsave_png : bool, optional\n Whether to save the plot as a PNG file. If `True`, the plot will be saved to the specified\n directory and filename. Default is `False`.\n\ndir_fname : str, optional\n The directory and filename for saving the PNG image if `save_png` is `True`.\n If left empty, no file is saved. Default is an empty string.\n\nReturns:\n--------\nNone\n This function does not return any value. It displays the mesh plot in a PyVista window\n and optionally saves a screenshot.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "plot_scalar", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 584, + "signature": "(mesh, scalar, scalar_name = '', cmap = '', clim = '', window_size = (750, 750), title = '', fmt = '%10.7f', clip_angle = 0.0, cpos = 'xy', show_edges = False, save_png = False, dir_fname = '')", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "scalar", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "scalar_name", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "cmap", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "clim", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "window_size", + "type_hint": null, + "default": "(750, 750)", + "description": "" + }, + { + "name": "title", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "fmt", + "type_hint": null, + "default": "'%10.7f'", + "description": "" + }, + { + "name": "clip_angle", + "type_hint": null, + "default": "0.0", + "description": "" + }, + { + "name": "cpos", + "type_hint": null, + "default": "'xy'", + "description": "" + }, + { + "name": "show_edges", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "save_png", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "dir_fname", + "type_hint": null, + "default": "''", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Plot a scalar quantity from a mesh with options for clipping, colormap, and saving.\n\nParameters:\n-----------\nmesh : object\n The mesh object to be plotted. This should be in a format that can be converted\n into a PyVista mesh using `vis.mesh_to_pv_mesh()`.\n\nscalar : mesh variable name or sympy expression\n The scalar values associated with the mesh points. These values will be visualized\n on the mesh.\n\nscalar_name : str, optional\n The name of the scalar field to be used when adding it to the mesh. This name will\n also be used as the label for the scalar bar. Default is an empty string.\n\ncmap : str, optional\n The colormap to be used for visualizing the scalar values. This can be any colormap\n recognized by PyVista or Matplotlib. Default is an empty string, which uses the default colormap.\n\nclim : tuple of float, optional\n The scalar range to be used for coloring the mesh (e.g., `(min_value, max_value)`). If not\n provided, the range of the scalar values is used. Default is an empty string, which uses\n the full range of the scalar values.\n\nwindow_size : tuple of int, optional\n The size of the rendering window in pixels as (width, height). Default is (750, 750).\n\ntitle : str, optional\n The title text to be displayed on the plot. Default is an empty string, meaning no title is shown.\n\nfmt : str, optional\n The format string for scalar values. This is typically used when displaying values on the scalar bar.\n Default is '%10.7f'.\n\nclip_angle : float, optional\n The angle (in degrees) at which to clip the mesh. If set to 0.0, no clipping is applied.\n Clipping is performed using planes at the specified angle. Default is 0.0.\n\ncpos : str or list, optional\n The camera position for viewing the mesh. It can be a string such as 'xy', 'xz', 'yz', or\n a list specifying the exact camera position. Default is 'xy'.\n\nshow_edges : bool, optional\n Whether to display the edges of the mesh in the plot. If `True`, edges will be shown.\n Default is `False`.\n\nsave_png : bool, optional\n Whether to save the plot as a PNG file. If `True`, the plot will be saved to the specified\n directory and filename. Default is `False`.\n\ndir_fname : str, optional\n The directory and filename for saving the PNG image if `save_png` is `True`.\n If left empty, no file is saved. Default is an empty string.\n\nReturns:\n--------\nNone\n This function does not return any value. It displays the scalar field on the mesh in a PyVista\n window and optionally saves a screenshot.", + "harvested_comments": [ + "Build scalar bar label with units if available" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "plot_vector", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 716, + "signature": "(mesh, vector, vector_name = '', cmap = '', clim = '', vmag = '', vfreq = '', save_png = False, dir_fname = '', title = '', fmt = '%10.7f', clip_angle = 0.0, show_arrows = False, cpos = 'xy', show_edges = False, window_size = (750, 750), scalar = None, scalar_name = '')", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vector_name", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "cmap", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "clim", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "vmag", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "vfreq", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "save_png", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "dir_fname", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "title", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "fmt", + "type_hint": null, + "default": "'%10.7f'", + "description": "" + }, + { + "name": "clip_angle", + "type_hint": null, + "default": "0.0", + "description": "" + }, + { + "name": "show_arrows", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "cpos", + "type_hint": null, + "default": "'xy'", + "description": "" + }, + { + "name": "show_edges", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "window_size", + "type_hint": null, + "default": "(750, 750)", + "description": "" + }, + { + "name": "scalar", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "scalar_name", + "type_hint": null, + "default": "''", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Plot a vector quantity from a mesh with options for clipping, colormap, vector magnitude, and saving.\n\nParameters:\n-----------\nmesh : object\n The mesh object to be plotted. This should be in a format that can be converted\n into a PyVista mesh using `vis.mesh_to_pv_mesh()`.\n\nvector : mesh variable name or sympy expression\n The symbolic representation of the vector field associated with the mesh points.\n This vector field will be visualized on the mesh.\n\nvector_name : str, optional\n The name of the vector field to be used when adding it to the mesh. This name will\n also be used as the label for the vector magnitude in the scalar bar. Default is an empty string.\n\ncmap : str, optional\n The colormap to be used for visualizing the vector magnitudes. This can be any colormap\n recognized by PyVista or Matplotlib. Default is an empty string, which uses the default colormap.\n\nclim : tuple of float, optional\n The scalar range to be used for coloring the mesh based on vector magnitudes (e.g., `(min_value, max_value)`).\n If not provided, the range of the vector magnitudes is used. Default is an empty string.\n\nvmag : float or str, optional\n The scaling factor for the arrow magnitudes when plotting vectors as arrows.\n Default is an empty string, which uses the default scaling.\n\nvfreq : int, optional\n The frequency of arrows to display when `show_arrows` is `True`. For example, if set to 10, every 10th vector\n will be plotted as an arrow. Default is an empty string, which uses the default frequency.\n\nsave_png : bool, optional\n Whether to save the plot as a PNG file. If `True`, the plot will be saved to the specified\n directory and filename. Default is `False`.\n\ndir_fname : str, optional\n The directory and filename for saving the PNG image if `save_png` is `True`.\n If left empty, no file is saved. Default is an empty string.\n\ntitle : str, optional\n The title text to be displayed on the plot. Default is an empty string, meaning no title is shown.\n\nfmt : str, optional\n The format string for scalar values, typically used in the scalar bar. Default is '%10.7f'.\n\nclip_angle : float, optional\n The angle (in degrees) at which to clip the mesh. If set to 0.0, no clipping is applied.\n Clipping is performed using planes at the specified angle. Default is 0.0.\n\nshow_arrows : bool, optional\n Whether to display arrows representing the vector field on the mesh. If `True`, arrows will be shown.\n Default is `False`.\n\ncpos : str or list, optional\n The camera position for viewing the mesh. It can be a string such as 'xy', 'xz', 'yz', or\n a list specifying the exact camera position. Default is 'xy'.\n\nshow_edges : bool, optional\n Whether to display the edges of the mesh in the plot. If `True`, edges will be shown.\n Default is `False`.\n\nwindow_size : tuple of int, optional\n The size of the rendering window in pixels as (width, height). Default is (750, 750).\n\nscalar : mesh variable name or sympy expression, optional\n An optional scalar field associated with the mesh points. If provided, this scalar field\n will be used for coloring the mesh instead of the vector magnitude. Default is `None`.\n\nscalar_name : str, optional\n The name of the scalar field to be used when adding it to the mesh. This name will\n be used as the label for the scalar bar if `scalar` is provided. Default is an empty string.\n\nReturns:\n--------\nNone\n This function does not return any value. It displays the vector field on the mesh in a PyVista\n window and optionally saves a screenshot.\n\nNotes\n-----\nWhen the model uses physical units, arrows are drawn in the mesh\ncoordinate space. A velocity of 1 cm/yr on a mesh in meters\n(extent ~1e6) produces arrows of length ~3e-10 in mesh units \u2014\neffectively invisible. Adjust ``vmag`` to compensate::\n\n # Scale arrows to ~5% of mesh extent\n vmag = 0.05 * mesh_extent / max_velocity", + "harvested_comments": [ + "Scale arrows to ~5% of mesh extent", + "Build scalar bar label with units if available" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "save_colorbar", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 899, + "signature": "(colormap = '', cb_bounds = None, vmin = None, vmax = None, figsize_cb = (6, 1), primary_fs = 18, cb_orient = 'vertical', cb_axis_label = '', cb_label_xpos = 0.5, cb_label_ypos = 0.5, fformat = 'png', output_path = '', fname = '')", + "parameters": [ + { + "name": "colormap", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "cb_bounds", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "vmin", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "vmax", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "figsize_cb", + "type_hint": null, + "default": "(6, 1)", + "description": "" + }, + { + "name": "primary_fs", + "type_hint": null, + "default": "18", + "description": "" + }, + { + "name": "cb_orient", + "type_hint": null, + "default": "'vertical'", + "description": "" + }, + { + "name": "cb_axis_label", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "cb_label_xpos", + "type_hint": null, + "default": "0.5", + "description": "" + }, + { + "name": "cb_label_ypos", + "type_hint": null, + "default": "0.5", + "description": "" + }, + { + "name": "fformat", + "type_hint": null, + "default": "'png'", + "description": "" + }, + { + "name": "output_path", + "type_hint": null, + "default": "''", + "description": "" + }, + { + "name": "fname", + "type_hint": null, + "default": "''", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Save a colorbar separately from a plot with customizable appearance and format.\n\nParameters:\n-----------\ncolormap : str, optional\n The name of the colormap to be used for the colorbar. This should be a valid Matplotlib colormap name.\n Default is an empty string, which uses the default colormap.\n\ncb_bounds : list or array-like, optional\n The bounds to be used for the colorbar. If provided, the colorbar will be generated with these bounds.\n Default is None, which means bounds are not explicitly set.\n\nvmin : float, optional\n The minimum value for the colorbar. This is used to define the lower limit of the colormap.\n Default is None.\n\nvmax : float, optional\n The maximum value for the colorbar. This is used to define the upper limit of the colormap.\n Default is None.\n\nfigsize_cb : tuple of float, optional\n The size of the figure for the colorbar in inches as (width, height). Default is (6, 1).\n\nprimary_fs : int, optional\n The primary font size for the colorbar labels and title. Default is 18.\n\ncb_orient : str, optional\n The orientation of the colorbar, either 'vertical' or 'horizontal'. Default is 'vertical'.\n\ncb_axis_label : str, optional\n The label for the colorbar axis. This text will be displayed alongside the colorbar.\n Default is an empty string.\n\ncb_label_xpos : float, optional\n The x-position for the colorbar label. This adjusts the horizontal positioning of the label.\n Default is 0.5.\n\ncb_label_ypos : float, optional\n The y-position for the colorbar label. This adjusts the vertical positioning of the label.\n Default is 0.5.\n\nfformat : str, optional\n The format for saving the colorbar image. Supported formats are 'png' and 'pdf'.\n Default is 'png'.\n\noutput_path : str, optional\n The directory path where the colorbar image will be saved. Default is an empty string.\n\nfname : str, optional\n The filename to use when saving the colorbar image. This should not include the file extension.\n Default is an empty string.\n\nReturns:\n--------\nNone\n This function does not return any value. It saves the colorbar as a separate image file in the specified format.", + "harvested_comments": [ + "Set font size" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": true + }, + { + "name": "_Unknowns", + "kind": "class", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 769, + "signature": "class _Unknowns:", + "parameters": [], + "returns": null, + "existing_docstring": "\n Manager for solver unknown variables and derived quantities.\n\n This class manages the primary unknown variable (e.g., velocity, temperature)\n and provides automatic computation of derived quantities like velocity gradients,\n strain rates, and vorticity.\n\n Attributes\n ----------\n u : MeshVariable\n The primary unknown variable being solved for.\n DuDt : SemiLagrangian_DDt, optional\n Time derivative manager for advection-diffusion problems.\n DFDt : SemiLagrangian_DDt, optional\n Flux time derivative manager for viscoelastic problems.\n L : sympy.Matrix\n Velocity gradient tensor :math:`L_{ij} = \\\\partial u_i / \\\\partial x_j`.\n E : sympy.Matrix\n Strain rate tensor :math:`E = (L + L^T) / 2` (symmetric part of L).\n W : sympy.Matrix\n Vorticity tensor :math:`W = (L - L^T) / 2` (antisymmetric part of L).\n Einv2 : sympy.Expr\n Second invariant of strain rate :math:`\\\\sqrt{E_{ij} E_{ij} / 2}`.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Unknowns", + "kind": "class", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5088, + "signature": "class _Unknowns(SolverBaseClass._Unknowns):", + "parameters": [], + "returns": null, + "existing_docstring": "Extend the unknowns with the constraint parameter", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_BlockConstraintBC", + "kind": "class", + "file": "src/underworld3/systems/solvers.py", + "line": 2041, + "signature": "class _BlockConstraintBC", + "parameters": [], + "returns": null, + "existing_docstring": "Bookkeeping for one in-saddle-point multiplier constraint.\n\nHolds the multiplier field ``lam`` (a full-domain scalar MeshVariable\nregistered as an extra DM field), the prescribed normal velocity ``g``, the\n(symbolic, row-vector) constraint normal, and the mutable PETSc bookkeeping\n(``petsc_id``/``label_val``) plus the JIT-compiled boundary functions\n(``fns``) populated during assembly. Mirrors the natural-BC namedtuple but\nstays a mutable object so the Cython assembly can stamp ids onto it.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 62, + "signature": "def __init__(self, mesh):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 795, + "signature": "def __init__(inner_self, _owning_solver):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2394, + "signature": "def __init__(self,\n mesh : uw.discretisation.Mesh,\n u_Field : uw.discretisation.MeshVariable = None,\n degree: int = 2,\n verbose = False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3330, + "signature": "def __init__(self,\n mesh : uw.discretisation.Mesh,\n u_Field : uw.discretisation.MeshVariable = None,\n degree = 2,\n verbose = False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4363, + "signature": "def __init__(self,\n mesh : uw.discretisation.Mesh,\n u_Field : uw.discretisation.MeshVariable = None,\n n_components : int = None,\n degree = 2,\n verbose = False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5091, + "signature": "def __init__(inner_self, owning_solver):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5113, + "signature": "def __init__(self,\n mesh : underworld3.discretisation.Mesh,\n velocityField : Optional[underworld3.discretisation.MeshVariable] = None,\n pressureField : Optional[underworld3.discretisation.MeshVariable] = None,\n degree : Optional[int] = 2,\n p_continuous : Optional[bool] = True,\n verbose : Optional[bool] =False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solver_template.py", + "line": 338, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: Optional[uw.discretisation.MeshVariable] = None, degree: int = 2, verbose: bool = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Initialize vector source term" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_MyVectorEquation", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 199, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Legacy positional order (mesh, u_Field, verbose, degree): the", + "bool in the degree slot is the legacy verbose flag; a legacy", + "positional degree, if present, landed in the verbose slot.", + "# Parent class will set up default values etc", + "default values for properties" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Poisson", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 415, + "signature": "(self, mesh: uw.discretisation.Mesh, h_Field: Optional[uw.discretisation.MeshVariable] = None, v_Field: Optional[uw.discretisation.MeshVariable] = None, degree: int = 2, verbose = False, DuDt = None, DFDt = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "h_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "v_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# Parent class will set up default values etc", + "default values for properties", + "# Set up the projection operator that", + "# solves the flow rate", + "If we add smoothing, it should be small" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Darcy", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 634, + "signature": "(self, mesh: uw.discretisation.Mesh, h_Field: Optional[uw.discretisation.MeshVariable] = None, v_Field: Optional[uw.discretisation.MeshVariable] = None, order: int = 1, theta: float = 0.5, degree: int = 2, verbose = False, DuDt = None, DFDt = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "h_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "v_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "order", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "theta", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_TransientDarcy", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 950, + "signature": "(self, mesh: uw.discretisation.Mesh, psi_Field: Optional[uw.discretisation.MeshVariable] = None, v_Field: Optional[uw.discretisation.MeshVariable] = None, order: int = 1, theta: float = 0.5, degree: int = 2, verbose = False, DuDt = None, DFDt = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "psi_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "v_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "order", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "theta", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "None \u2192 head-based form; set \u2192 mixed form" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Richards", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 1133, + "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: Optional[uw.discretisation.MeshVariable] = None, pressureField: Optional[uw.discretisation.MeshVariable] = None, degree: Optional[int] = 2, p_continuous: Optional[bool] = True, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "velocityField", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "pressureField", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "Optional[int]", + "default": "2", + "description": "" + }, + { + "name": "p_continuous", + "type_hint": "Optional[bool]", + "default": "True", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Not used in Stokes, but may be used in NS, VE etc", + "User-facing operations are matrices / vectors by preference", + "by default, incompressibility constraint", + "this attrib records if we need to setup the problem (again)", + "Optional: alternative F1 expression to autodiff for the" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Stokes", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 1996, + "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: Optional[uw.discretisation.MeshVariable] = None, pressureField: Optional[uw.discretisation.MeshVariable] = None, degree: Optional[int] = 2, order: Optional[int] = 2, p_continuous: Optional[bool] = True, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "velocityField", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "pressureField", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "Optional[int]", + "default": "2", + "description": "" + }, + { + "name": "order", + "type_hint": "Optional[int]", + "default": "2", + "description": "" + }, + { + "name": "p_continuous", + "type_hint": "Optional[bool]", + "default": "True", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Pre-create DFDt so it's available before constitutive model is set" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_VE_Stokes", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2055, + "signature": "(self, boundary, g, normal, lam, augmentation)", + "parameters": [ + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "g", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "normal", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "lam", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "augmentation", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Augmented-Lagrangian parameter r: a penalty r(n\u00b7u\u2212g)\u00b7n added to the", + "u-row, giving a uu boundary stiffness r\u00b7(n\u2297n) that conditions the", + "[p,h] Schur complement WITHOUT biasing the multiplier (the h-row is", + "still the exact constraint). 0 disables it (bare KKT).", + "PETSc needs ONE boundary entry per (boundary, test-field): each entry" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_BlockConstraintBC", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2125, + "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: Optional[uw.discretisation.MeshVariable] = None, pressureField: Optional[uw.discretisation.MeshVariable] = None, degree: Optional[int] = 2, p_continuous: Optional[bool] = True, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "velocityField", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "pressureField", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "Optional[int]", + "default": "2", + "description": "" + }, + { + "name": "p_continuous", + "type_hint": "Optional[bool]", + "default": "True", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "In-saddle multiplier constraints (see add_constraint_bc). Empty for an", + "ordinary Stokes solve, so the base assembly is unaffected.", + "Guarded automatic pressure gauge (see _maybe_install_auto_gauge). On an", + "enclosed constrained problem the constant pressure and constant", + "multiplier are gauge-free; the solver lands on a partition-dependent" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Stokes_Constrained", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2653, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Default: project zero" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Projection", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2968, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Default: project zero vector" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Vector_Projection", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3179, + "signature": "(self, mesh: uw.discretisation.Mesh, tensor_Field: uw.discretisation.MeshVariable = None, scalar_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "tensor_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": "None", + "description": "" + }, + { + "name": "scalar_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Tensor_Projection", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3330, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, n_components: int = None, degree = 2, verbose = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": "None", + "description": "" + }, + { + "name": "n_components", + "type_hint": "int", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_MultiComponent_Projection", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3612, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable, V_fn: Union[uw.discretisation.MeshVariable, sympy.Basic], order: int = 1, restore_points_func: Callable = None, verbose = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, monotone_mode: Optional[str] = None, theta: float = 0.5, old_frame_traceback: bool = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": null, + "description": "" + }, + { + "name": "V_fn", + "type_hint": "Union[uw.discretisation.MeshVariable, sympy.Basic]", + "default": null, + "description": "" + }, + { + "name": "order", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "restore_points_func", + "type_hint": "Callable", + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "monotone_mode", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "theta", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "old_frame_traceback", + "type_hint": "bool", + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Should be a sympy function", + "# Parent class will set up default values etc", + "default values for properties", + "These are unique to the advection solver", + "## Setup the history terms ... This version should not build anything" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_AdvectionDiffusion", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4210, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable, order: int = 1, theta: float = 0.0, evalf: Optional[bool] = False, verbose = False, DuDt: Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "uw.discretisation.MeshVariable", + "default": null, + "description": "" + }, + { + "name": "order", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "theta", + "type_hint": "float", + "default": "0.0", + "description": "" + }, + { + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# Parent class will set up default values etc", + "default values for properties", + "These are unique to the advection solver", + "## Setup the history terms ... This version should not build anything", + "## by default - it's the template / skeleton" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Diffusion", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4622, + "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: uw.discretisation.MeshVariable, pressureField: uw.discretisation.MeshVariable, rho: Optional[float] = 0.0, restore_points_func: Callable = None, order: Optional[int] = 2, flux_order: Optional[int] = None, p_continuous: Optional[bool] = False, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "velocityField", + "type_hint": "uw.discretisation.MeshVariable", + "default": null, + "description": "" + }, + { + "name": "pressureField", + "type_hint": "uw.discretisation.MeshVariable", + "default": null, + "description": "" + }, + { + "name": "rho", + "type_hint": "Optional[float]", + "default": "0.0", + "description": "" + }, + { + "name": "restore_points_func", + "type_hint": "Callable", + "default": "None", + "description": "" + }, + { + "name": "order", + "type_hint": "Optional[int]", + "default": "2", + "description": "" + }, + { + "name": "flux_order", + "type_hint": "Optional[int]", + "default": "None", + "description": "" + }, + { + "name": "p_continuous", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# Parent class will set up default values and load u_Field into the solver", + "These are unique to the advection solver", + "None means follow effective_order", + "self._E = self.mesh.vector.strain_tensor(self.u.sym)", + "## sets up DuDt and DFDt" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2166, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2242, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/solver_template.py", + "line": 86, + "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: Optional[uw.discretisation.MeshVariable] = None, degree: int = 2, verbose: bool = False, DuDt: Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]] = None, DFDt: Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]] = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "u_Field", + "type_hint": "Optional[uw.discretisation.MeshVariable]", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "2", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "DuDt", + "type_hint": "Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]]", + "default": "None", + "description": "" + }, + { + "name": "DFDt", + "type_hint": "Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]]", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Initialize the solver.\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The computational mesh\nu_Field : uw.discretisation.MeshVariable, optional\n Solution field (created if None)\ndegree : int\n Polynomial degree for basis functions\nverbose : bool\n Enable verbose output\nDuDt : DDt object, optional\n Time derivative method for solution\nDFDt : DDt object, optional\n Time derivative method for flux", + "harvested_comments": [ + "Track instances", + "Initialize parent class", + "Initialize default property values", + "Source term", + "Problem-specific parameters" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "SNES_MyEquation", + "is_public": false + }, + { + "name": "_ParameterBase", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 60, + "signature": "class _ParameterBase", + "parameters": [], + "returns": null, + "existing_docstring": "Base class for all constitutive model ``_Parameters`` containers.\n\nAll ``_Parameters`` nested classes must inherit from this. It provides a\n``__setattr__`` guard that **rejects** any public attribute assignment\nthat doesn't match a defined ``Parameter`` descriptor or ``@property``\non the class. Without this guard, a typo like\n``Parameters.viscocity = 1`` silently creates an instance attribute\nthat the solver never reads \u2014 producing wrong results with no error.\n\nHow to define parameters in a new constitutive model\n----------------------------------------------------\n1. Inherit from ``_ParameterBase`` (and ``_ViscousParameterAlias`` if\n the model has a ``shear_viscosity_0`` parameter)::\n\n class _Parameters(_ParameterBase, _ViscousParameterAlias):\n ...\n\n2. Define each parameter as a **class-level** ``Parameter`` descriptor.\n The **attribute name IS the user API name** \u2014 users will set it via\n ``model.Parameters. = value``::\n\n import underworld3.utilities._api_tools as api_tools\n\n shear_viscosity_0 = api_tools.Parameter(\n r\"\\eta\", # LaTeX display name (cosmetic only)\n lambda self: 1, # default value factory\n \"Shear viscosity\", # description\n units=\"Pa*s\", # expected units\n )\n\n3. To add a **convenience alias** (e.g. ``viscosity`` \u2192 ``shear_viscosity_0``),\n either use a mixin like ``_ViscousParameterAlias`` or define a\n ``@property`` with getter and setter on the ``_Parameters`` class.\n The guard recognises both descriptors and properties.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_ViscousParameterAlias", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 142, + "signature": "class _ViscousParameterAlias", + "parameters": [], + "returns": null, + "existing_docstring": "Mixin providing ``viscosity`` as a read/write alias for ``shear_viscosity_0``.\n\nAdd this to the inheritance of any ``_Parameters`` class that defines\na ``shear_viscosity_0`` descriptor, so that the established\n``Parameters.viscosity`` API continues to work::\n\n class _Parameters(_ParameterBase, _ViscousParameterAlias):\n shear_viscosity_0 = api_tools.Parameter(...)\n\nTo create similar aliases for other parameters, define a ``@property``\nwith a setter \u2014 the ``_ParameterBase`` guard recognises properties\nautomatically.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 379, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 750, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nNow uses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 966, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\n`sympy.oo` (infinity) for default values ensures that sympy.Min simplifies away\nthe conditionals when they are not required.\n\nUses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 1240, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nUses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2079, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nNow uses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2343, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nUses Parameter descriptor pattern for scalar permeability.\nMatrix-valued `s` remains instance-level (special case).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2502, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nUses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_Parameters", + "kind": "class", + "file": "src/underworld3/constitutive_models.py", + "line": 2808, + "signature": "class _Parameters", + "parameters": [], + "returns": null, + "existing_docstring": "Parameters for transverse isotropic VEP model.\n\nCombines anisotropic parameters (\u03b7\u2080, \u03b7\u2081, director) with VEP\nparameters (shear_modulus, yield_stress, etc.).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_reset", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 893, + "signature": "def _reset(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1227, + "signature": "def _build(self,\n verbose: bool = False,\n debug: bool = False,\n debug_name: str = None,\n ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_problem_description", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1437, + "signature": "def _setup_problem_description(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_discretisation", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2604, + "signature": "def _setup_discretisation(self, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_pointwise_functions", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2793, + "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_solver", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2951, + "signature": "def _setup_solver(self, verbose=False, _rewire_only=False):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_pointwise_functions", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3766, + "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_solver", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4002, + "signature": "def _setup_solver(self, verbose=False, _rewire_only=False):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_discretisation", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4464, + "signature": "def _setup_discretisation(self, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_pointwise_functions", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4590, + "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_solver", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4792, + "signature": "def _setup_solver(self, verbose=False, _rewire_only=False):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4981, + "signature": "def _object_viewer(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_history_terms", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5732, + "signature": "def _setup_history_terms(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_reset_stokes_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6141, + "signature": "def _reset_stokes_nullspace(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_pointwise_functions", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6559, + "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_solver", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7304, + "signature": "def _setup_solver(self, verbose=False, _rewire_only=False):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_BaseMeshVariable", + "kind": "class", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 73, + "signature": "class _BaseMeshVariable", + "parameters": [], + "returns": null, + "existing_docstring": "The MeshVariable class generates a variable supported by a finite element mesh and the\nunderlying sympy representation that makes it possible to construct expressions that\ndepend on the values of the MeshVariable.\n\nTo set / read nodal values, use the numpy interface via the 'data' property.\n\nParameters\n----------\nvarname :\n A text name for this variable. Use an R-string if a latex-expression is used\nmesh :\n The supporting underworld mesh.\nnum_components :\n The number of components this variable has.\n For example, scalars will have `num_components=1`,\n while a 2d vector would have `num_components=2`.\nvtype :\n Optional. The underworld variable type for this variable.\n If not defined it will be inferred from `num_components`\n if possible.\ndegree :\n The polynomial degree for this variable.\nvarsymbol:\n Over-ride the varname with a symbolic form for printing etc (latex). Should be an R-string.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_DDtCoreState", + "kind": "class", + "file": "src/underworld3/systems/ddt.py", + "line": 85, + "signature": "class _DDtCoreState", + "parameters": [], + "returns": null, + "existing_docstring": "Common evolution-tracking fields shared by every DDt flavor.\n\nEach concrete flavor extends this with its own psi_star\nrepresentation (sympy expressions for Symbolic; mesh-variable\nnames for Eulerian / SemiLagrangian; swarm-variable names for\nLagrangian / Lagrangian_Swarm). The actual variable DOF / particle\ndata lives in the mesh-variable or swarm-variable path of the\nsnapshot \u2014 these State dataclasses carry only the metadata needed\nto re-bind on restore.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 3601, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_AdvectionDiffusion", + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4200, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_Diffusion", + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 4610, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SNES_NavierStokes", + "is_public": false + }, + { + "name": "_PintHelper", + "kind": "class", + "file": "src/underworld3/units.py", + "line": 49, + "signature": "class _PintHelper", + "parameters": [], + "returns": null, + "existing_docstring": "Simple helper for Pint operations.\n\nThis replaces the deprecated PintBackend class with direct Pint usage.\nProvides a minimal interface for the few places that need backend-like operations.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_JITConstant", + "kind": "class", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 303, + "signature": "class _JITConstant", + "parameters": [], + "returns": null, + "existing_docstring": "Symbol subclass that renders as constants[i] in generated C code.\n\nUsed by the JIT compiler to route constant UWexpressions through\nPETSc's PetscDSSetConstants() mechanism instead of baking values\nas C literals.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_uw_record", + "kind": "class", + "file": "src/underworld3/utilities/_utils.py", + "line": 9, + "signature": "class _uw_record", + "parameters": [], + "returns": null, + "existing_docstring": "A class to record runtime information about the underworld3 execution environment.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/checkpoint/backend.py", + "line": 52, + "signature": "(self) -> None", + "parameters": [], + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "InMemoryBackend", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 60, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "_managed must exist before any public attribute assignment", + "routes through __setattr__.", + "uw_object: sets self._uw_id (underscore)" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ModelTracker", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 385, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 735, + "signature": "(self, unknowns, material_name: str = None)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "material_name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "All this needs to do is define the", + "viscosity property and init the parent(s)", + "In this case, nothing seems to be needed.", + "The viscosity is completely defined", + "in terms of the Parameters" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ViscousFlowModel", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 773, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 946, + "signature": "(self, unknowns, material_name: str = None)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "material_name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "All this needs to do is define the", + "non-paramter properties that we want to", + "use in other expressions and init the parent(s)" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ViscoPlasticFlowModel", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1016, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1313, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Internal symbols for stress history (not parameters, internal state)", + "# The following expressions are containers for derived/computed values.", + "# They have @property calls to retrieve / calculate them.", + "# We keep them as expression containers for lazy evaluation." + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2038, + "signature": "(self, unknowns, material_name = None)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "material_name", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "MaxwellExponentialFlowModel", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2102, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2167, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Set default diffusivity as an identity matrix wrapped in an expression", + "Store the validated diffusivity as a diagonal matrix" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2243, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2365, + "signature": "(inner_self, _owning_model, permeabililty: Union[float, sympy.Function] = 1)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "permeabililty", + "type_hint": "Union[float, sympy.Function]", + "default": "1", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Note: typo in param name preserved for compatibility", + "Row matrix (1, dim) to match grad_u from jacobian" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2487, + "signature": "(self, unknowns, material_name: str = None)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "material_name", + "type_hint": "str", + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "All this needs to do is define the", + "viscosity property and init the parent(s)", + "In this case, nothing seems to be needed.", + "The viscosity is completely defined", + "in terms of the Parameters" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "TransverseIsotropicFlowModel", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2535, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2874, + "signature": "(inner_self, _owning_model)", + "parameters": [ + { + "name": "inner_self", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "_owning_model", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_Parameters", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3514, + "signature": "(self, unknowns, material_name = None)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "material_name", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "TransverseIsotropicMaxwellExponentialFlowModel", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3595, + "signature": "(self, unknowns, material_name = None)", + "parameters": [ + { + "name": "unknowns", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "material_name", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "TransverseIsotropicVEPSplitFlowModel", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 93, + "signature": "(self, index, system, pretty_str = None, latex_str = None, mesh = None, axis_index = None)", + "parameters": [ + { + "name": "index", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "system", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pretty_str", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "latex_str", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "axis_index", + "type_hint": null, + "default": "None", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Store UW3-specific attributes", + "Cache the original BaseScalar for equality comparison", + "This is what makes sympy.diff() work!", + "Track for debugging" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UWCoordinate", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 1351, + "signature": "(self, mesh, system: Optional[CoordinateSystemType] = CoordinateSystemType.CARTESIAN)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "system", + "type_hint": "Optional[CoordinateSystemType]", + "default": "CoordinateSystemType.CARTESIAN", + "description": "" + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Guard against SymPy trying to construct a CoordinateSystem from sympified elements", + "SymPy may iterate over the object and try to recreate it from elements", + "are the mesh coordinates XYZ or have they been replaced by", + "\"Natural\" coordinates like r, theta, z ?", + "Get the raw BaseScalars from the mesh" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", + "is_public": false + }, + { + "name": "_jacobian_unwrap", + "kind": "function", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 24, + "signature": "def _jacobian_unwrap(expr):", + "parameters": [], + "returns": null, + "existing_docstring": "Expand UWexpressions down to (but NOT including) constant atoms, for use\n as the input to a Jacobian derivative (``derive_by_array`` / ``diff``).\n\n Applied element-wise over a sympy ``Matrix``/``Array`` so atoms embedded in\n the residual flux are reached. Non-constant UWexpressions (e.g. the\n effective viscosity ``Min(eta0, tau_y/2/eps_II)``) are expanded so the\n derivative sees their field / grad-v dependence and forms the full Newton\n tangent. Truly-constant atoms (``eta0``, ``tau_y``, ...) are kept as the\n *same* symbol object so the JIT ``constants[]`` runtime-update mechanism is\n preserved \u2014 the keep-constants predicate is shared with\n ``getext()._extract_constants`` so the two cannot drift apart.\n\n This is a no-op for constant-viscosity problems (eta has no grad-v\n dependence), so those Jacobians stay bit-identical.\n\n See ``docs/developer/design/jacobian-unwrap-constants-bug.md``.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_jacobian_source", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 195, + "signature": "def _jacobian_source(self, expr, newton_expr=None):", + "parameters": [], + "returns": null, + "existing_docstring": "Prepare a residual flux for Jacobian differentiation.\n\n ``expr`` is the exact (Picard) flux; ``newton_expr`` is the consistent\n (Newton) flux to use \u2014 when None it is the unwrapped ``expr`` (the\n derivative then captures d(eta)/d(grad v)). The residual itself is never\n passed through here, so the converged solution always satisfies the\n exact constitutive law.\n\n Returns, by ``consistent_jacobian`` mode:\n * False -> ``expr`` (frozen viscosity; bit-identical Picard tangent)\n * True -> ``newton_expr`` (full consistent Newton tangent)\n * \"continuation\" -> ``expr + alpha*(newton_expr - expr)`` with alpha a\n constants[] parameter ramped 0->1 at solve time. Differentiating\n this gives ``G_picard + alpha*(G_newton - G_picard)`` because alpha\n is constant w.r.t. the unknowns. alpha=0 is bit-identical to Picard.\n\n No-op for constant viscosity (``newton_expr`` == ``expr``).\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_newton_flux", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 236, + "signature": "def _newton_flux(self, exact_F1):", + "parameters": [], + "returns": null, + "existing_docstring": "Newton (consistent) flux for the bulk ``F1`` Jacobian source.\n\n Returns the constitutive model's own smooth tangent law\n (``constitutive_model.flux_jacobian``) when it supplies one \u2014 matched to\n the container/shape of ``exact_F1`` \u2014 otherwise ``None`` so that\n :meth:`_jacobian_source` falls back to the exact flux unwrapped. Lets a\n model whose flux has a non-smooth yield kink provide a physically\n motivated smooth tangent without changing the residual.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_get_newton_alpha", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 266, + "signature": "def _get_newton_alpha(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Lazily construct the Picard->Newton continuation parameter.\n\n Built on first use (continuation mode only) so the default Picard path\n creates no extra UWexpression and leaves global symbol-naming / JIT-cache\n state byte-identical to historical behaviour.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_set_newton_alpha", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 280, + "signature": "def _set_newton_alpha(self, value):", + "parameters": [], + "returns": null, + "existing_docstring": "Set the Picard->Newton continuation fraction and push it to the DS.\n\n alpha is routed through ``constants[]`` (it appears as a constant atom\n in the blended Jacobian), so this updates the live tangent WITHOUT a JIT\n recompile. No-op outside ``consistent_jacobian == \"continuation\"`` (alpha\n is then absent from the constants manifest).\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_maybe_install_snes_update", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 407, + "signature": "def _maybe_install_snes_update(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Attach the SNESSetUpdate dispatcher iff callbacks are registered.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_scatter_global_to_fields", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 412, + "signature": "def _scatter_global_to_fields(self, gvec):", + "parameters": [], + "returns": null, + "existing_docstring": "Scatter the global iterate into the solver's field MeshVariable,\n correct on driven (non-zero Dirichlet) boundaries.\n\n Single-field solvers (scalar / vector / multi-component) store the whole\n solution in ``self.u`` on ``self.dm``. A plain ``globalToLocal`` leaves a\n field's non-zero Dirichlet (driven) boundary DOFs stale \u2014 those are not in\n the global vector; they are imposed on the LOCAL vector by\n ``DMPlexSNESComputeBoundaryFEM``. So a callback reading the field on a\n driven boundary would otherwise see a wrong value. This mirrors the\n post-solve copy-back: globalToLocal -> boundary FEM -> copy into the field.\n\n Stokes overrides this to split its multi-field DM (see\n ``SNES_Stokes_SaddlePt._scatter_global_to_fields``).\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_gather_fields_to_global", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 441, + "signature": "def _gather_fields_to_global(self, gvec):", + "parameters": [], + "returns": null, + "existing_docstring": "Gather the solver's field MeshVariable back into the global iterate.\n\n Single-field default (``localToGlobal`` drops the boundary DOFs, which are\n re-imposed next iteration). Stokes overrides for its multi-field DM.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_refresh_auxiliary_vec", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 449, + "signature": "def _refresh_auxiliary_vec(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Rebuild the mesh auxiliary vector so the residual / nested solves see the\ncurrent field values (callbacks may have changed v, p, or auxiliary fields).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_dispatch_snes_update", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 455, + "signature": "def _dispatch_snes_update(self, snes, iteration):", + "parameters": [], + "returns": null, + "existing_docstring": "PETSc SNESSetUpdate hook: sync iterate->fields, run callbacks, sync back.\n\n The mesh auxiliary vector is refreshed (a) before callbacks, so a callback\n reading v/p (e.g. a Helmholtz/Projection smoother) sees the current iterate,\n and (b) after, so the outer residual sees any fields the callbacks updated.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_apply_preconditioner_options", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 509, + "signature": "def _apply_preconditioner_options(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Push the PETSc option bundle implied by ``self.preconditioner``.\n\n Called from ``_build`` so that ``\"auto\"`` re-resolves against the\n *current* mesh \u2014 e.g. after a remesh that collapses the refinement\n hierarchy. Solvers opt in by setting ``self._pc_option_prefix`` (``\"\"``\n or ``\"fieldsplit_velocity_\"``); the default (None) makes this a no-op.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_enforce_galerkin_for_geometric_mg", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 633, + "signature": "def _enforce_galerkin_for_geometric_mg(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Geometric multigrid in UW3 REQUIRES Galerkin (RAP) coarse operators.\n\n UW3 installs no residual/Jacobian callbacks on the coarse DMs of the\n refinement hierarchy, so PETSc cannot re-discretise the operator there.\n With Galerkin RAP the coarse operators are assembled as R*A*P from the\n fine operator and the coarse DMs are used only for interpolation \u2014 which\n is the only mode that works. If ``pc_type=mg`` is selected on a block we\n manage but Galerkin is unset (or explicitly ``none``), PETSc instead\n tries to build coarse operators itself and fails cryptically: PETSc\n error 73 (\"KSPSetDM without ComputeOperators\") in serial, or\n ``DMCoarsen -> DMAdaptMetric -> ParMmg`` (which is 3D-only) in parallel.\n\n So rather than let users trip those, force ``pc_mg_galerkin=both``\n whenever a managed block uses geometric MG, regardless of who selected\n it (user, harness, or our own auto/fmg bundle). A bare ``=None`` flag\n already engages RAP (reads back as ``''`` but is *present*); only a\n genuinely-unset or ``none`` value is overridden.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_check_expression_meshes", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 672, + "signature": "def _check_expression_meshes(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Check that all MeshVariable symbols in solver expressions\n belong to this solver's mesh.\n\n Raises a clear error if variables from a different mesh are\n found, rather than letting the JIT fail with a cryptic message.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 881, + "signature": "def _object_viewer(self):", + "parameters": [], + "returns": null, + "existing_docstring": "This will add specific information about this object to the generic class viewer\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_warn_on_divergence", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1119, + "signature": "def _warn_on_divergence(self, phase=\"solve\"):", + "parameters": [], + "returns": null, + "existing_docstring": "Check SNES convergence and warn on genuine divergence.\n\n Parameters\n ----------\n phase : str\n ``\"picard\"`` -- ignore ``DIVERGED_MAX_IT`` (truncation is\n intentional during the Picard-to-Newton transition).\n ``\"solve\"`` -- any divergence is a real problem.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_snes_solve_with_retries", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1150, + "signature": "def _snes_solve_with_retries(self, gvec, divergence_retries=0, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": "Call self.snes.solve(None, gvec) with warm-start retries on divergence.\n\n Useful for nonlinear problems whose residual has kinks (e.g. VEP at\n the yield surface Min/softmin cutoff) where Newton can land on a\n bad iterate that trips DIVERGED_MAX_IT or DIVERGED_LINE_SEARCH. A\n single warm-start re-solve from the just-computed iterate commonly\n steps off the kink.\n\n The first solve is always performed. If the SNES converged reason is\n negative, up to ``divergence_retries`` additional calls to\n ``snes.solve(None, gvec)`` are made; ``gvec`` is retained between\n calls so each retry is a warm start. Returns once the SNES reports\n converged or the retry budget is exhausted.\n\n Parameters\n ----------\n gvec : PETSc.Vec\n Global solution vector (modified in place by snes.solve).\n divergence_retries : int, default=0\n Maximum retries on DIVERGED. 0 preserves legacy behaviour.\n Typically 1 is enough for VEP kink-related divergence.\n verbose : bool, default=False\n Log each retry on rank 0.\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_continuation_solve", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1196, + "signature": "def _continuation_solve(self, gvec, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": "Picard -> Newton continuation via the constants[]-routed alpha.\n\n Stage 1 solves with the frozen (Picard) tangent (alpha=0) to a loose\n tolerance to enter Newton's basin of attraction; stage 2 ramps to the\n consistent (Newton) tangent (alpha=1) and warm-starts to the requested\n tolerance. alpha is toggled through ``constants[]`` so neither stage\n triggers a JIT recompile (cf. Spiegelman et al. 2016; ASPECT).\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_set_constants_on_ds", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1393, + "signature": "def _set_constants_on_ds(self, ds):", + "parameters": [], + "returns": null, + "existing_docstring": "Pack current constant values and call PetscDSSetConstants.\n\n Parameters\n ----------\n ds : PETSc DS object\n The PetscDS to set constants on.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_update_constants", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1414, + "signature": "def _update_constants(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Re-pack current UWexpression values and call PetscDSSetConstants.\n\n Called before each solve() to ensure constants are current without\n requiring JIT recompilation.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_value_first_bc_args", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1610, + "signature": "def _value_first_bc_args(self, method, conds, boundary, alias=None,\n alias_name=\"g\"):", + "parameters": [], + "returns": null, + "existing_docstring": "Normalize BC arguments to the canonical value-first order.\n\n The canonical BC signature is ``method(conds, boundary, ...)`` with the\n prescribed datum named ``conds`` (Style Charter, API conventions;\n maintainer decisions D2/D3, 2026-07). Two legacy spellings are shimmed\n here, each with exactly one DeprecationWarning:\n\n * **boundary-first order** \u2014 detected conservatively: the first\n positional argument is a string (a boundary label; a BC datum is\n never a string \u2014 see :meth:`add_condition`) while the second is not.\n The two arguments are swapped.\n * **the** ``g=`` **keyword alias** for the datum \u2014 forwarded to\n ``conds``. Supplying both ``conds`` and ``g`` is an error.\n\n Returns the normalized ``(conds, boundary)`` pair; ``boundary`` is\n required to be a string on exit.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_tau_projection", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 1992, + "signature": "def _setup_tau_projection(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Create the mesh variable and projector for tau (lazy init).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_get_dof_partition_by_field_id", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2122, + "signature": "def _get_dof_partition_by_field_id(self,\n section_type: str,\n field_id: int,\n filename: Optional[str | None] = None,\n outputPath: Optional[str] = \"\"):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Private version of get_dof_partition with field_id as an additional parameter.\n Parameters\n ----------\n section_type:\n Can be: \"local\" which includes DOFs from ghost points or \"global\" which differentiates DOFs from ghost points by having negative values.\n field_id:\n The field id\n filename:\n Output file name. If None, will print out results; if set to a string, resulting h5 file has the following keys: field_id, rank, dof.\n outputPath:\n Path of directory where data is saved. If left empty it will save the data in the current working directory.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_assemble_volume_reaction", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2211, + "signature": "def _assemble_volume_reaction(self, time=None, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": "RAW per-rank FEM VOLUME residual (no boundary terms) in the DM-local layout,\n as a numpy array.\n\n At an essential-BC (Dirichlet) node this residual IS the consistent boundary\n reaction \u2014 the integrated nodal flux :math:`\\\\int_\\\\Gamma (F\\\\cdot\\\\hat n)\\\\phi_i`\n (heat flux for a scalar diffusion solve, traction for Stokes). Interior nodes are\n ~0. General across scalar / vector / Stokes solvers: the current solution is\n gathered from ``self.fields`` when present (Stokes) else the single\n ``Unknowns.u`` field.\n\n NOTE: the returned array is NOT globally assembled. The DM has overlap=0, so each\n rank computes only its OWNED cells' contribution; a boundary node shared across a\n partition cut therefore holds only this rank's PARTIAL reaction. The complete\n reaction is assembled by the caller (``utilities.boundary_flux._desmear``) by\n SUMMING each rank's partial by coordinate \u2014 not by a hand-rolled localToGlobal.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_attach_constant_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 2505, + "signature": "def _attach_constant_nullspace(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Attach a constant nullspace to the (already set-up) SNES\nJacobian. Scalar analogue of ``_attach_stokes_nullspace``.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3209, + "signature": "def _object_viewer(self):", + "parameters": [], + "returns": null, + "existing_docstring": "This will add specific information about this object to the generic class viewer\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_discretisation", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 3616, + "signature": "def _setup_discretisation(self, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Most of what is in the init phase that is not called by _setup_terms()\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 4289, + "signature": "def _object_viewer(self):", + "parameters": [], + "returns": null, + "existing_docstring": "This will add specific information about this object to the generic class viewer\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_residual_is_nonlinear", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 5412, + "signature": "def _residual_is_nonlinear(self, tol=1e-8):", + "parameters": [], + "returns": null, + "existing_docstring": "True if the Stokes residual is nonlinear in the unknowns :math:`(v, p)`\n \u2014 i.e. the assembled Jacobian depends on the solution, so Newton / Picard\n iteration is required.\n\n Detected by a NUMERICAL probe: assemble the Jacobian at two distinct\n velocity states and compare. A symbolic test on ``F1.sym`` cannot see the\n nonlinearity \u2014 the effective viscosity's strain-rate (velocity-gradient)\n dependence is carried as a JIT-substituted *placeholder* symbol\n (``\\dot\\varepsilon_{II}``) in the flux, decoupled from the gradient\n ``L`` in the symbolic form, so it only becomes visible once the operator\n is assembled at a concrete iterate. Constant- or temperature-dependent\n viscosity \u21d2 ``J`` independent of ``v`` \u21d2 the two assemblies are\n bit-identical \u21d2 linear.\n\n Used to fail-fast on the rotated-free-slip path, which is a single linear\n solve (assemble ``J(0)``, ``F(0)`` once) and would otherwise SILENTLY\n return one Newton linearisation from ``u=0`` for a nonlinear model. The\n caller must have run the pre-solve preamble (auxiliary vector + constants)\n so the assembly sees the correct coefficients.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_pressure_dirichlet_bcs", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6149, + "signature": "def _pressure_dirichlet_bcs(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Return essential boundary conditions applied to the pressure field.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_pressure_nullspace_vector", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6158, + "signature": "def _build_pressure_nullspace_vector(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Create the constant-pressure basis vector for the coupled Stokes DM.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_velocity_nullspace_vector", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6176, + "signature": "def _build_velocity_nullspace_vector(self, mode):", + "parameters": [], + "returns": null, + "existing_docstring": "Create a velocity nullspace basis vector from a user-supplied mode.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_block_gauge_nullspace_vector", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6211, + "signature": "def _build_block_gauge_nullspace_vector(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Combined constant-(pressure, multiplier) gauge mode (block-constrained).\n\n On a free-slip (non-Dirichlet) boundary delta_u.n != 0, so\n int_Gamma n.delta_u = int_Omega div(delta_u) != 0, and a constant\n pressure (B\u1d401) and a constant multiplier (C\u1d401) couple to the SAME u-row\n functional. The genuine near-null mode is therefore the COMBINED\n (p = +1, h = -1 everywhere, u = 0): the u-row contribution\n (1)\u00b7int n.delta_u + (-1)\u00b7int n.delta_u cancels, the h-row leaves only\n eM\u00b7(-1) ~ 0. This replaces the pure constant-pressure mode for the block\n solver (which is NOT a null mode when the constraint boundary is free).\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_stokes_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6244, + "signature": "def _build_stokes_nullspace(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Create the configured coupled Stokes nullspace basis.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_block_fieldsplit_options", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6287, + "signature": "def _setup_block_fieldsplit_options(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Collapse the 3+ field DM to a 2-way velocity | [p,h] Schur split.\n\n We group by DM FIELD INDEX (pc_fieldsplit_0_fields=0,\n pc_fieldsplit_1_fields=1,2,...) rather than by IS: a field-index split\n keeps the DM-field association, so geometric multigrid / FMG on the\n velocity block can build its interpolation hierarchy (an IS-defined\n split has no DM and PCMG errors out with PETSC_ERR_SUP). The grouped\n splits are named \"0\"/\"1\", so we MIRROR the user-facing\n fieldsplit_velocity_* / fieldsplit_pressure_* options (defaults + any\n user/FMG overrides) onto fieldsplit_0_* / fieldsplit_1_* \u2014 existing\n configs and FMG harnesses then apply unchanged. Must run before\n setFromOptions. No-op if the user chose a non-fieldsplit pc_type.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_attach_stokes_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6322, + "signature": "def _attach_stokes_nullspace(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Attach the configured coupled Stokes nullspace to the solver matrices.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_velocity_rotation_nullspace", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6368, + "signature": "def _build_velocity_rotation_nullspace(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Cache a velocity-rotation-only ``MatNullSpace`` (zero in pressure /\n multiplier blocks). Used to project the rigid-rotation gauge out of the\n converged solution \u2014 see ``_remove_velocity_rotation_gauge``. Cached;\n the cache is invalidated by ``_reset_stokes_nullspace`` / solver rebuild\nbecause the modes depend on node coordinates.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_remove_velocity_rotation_gauge", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6398, + "signature": "def _remove_velocity_rotation_gauge(self, gvec):", + "parameters": [], + "returns": null, + "existing_docstring": "Project the rigid-body rotation gauge out of the converged solution.\n\n For a free-slip (non-Dirichlet) velocity, the rigid rotations are a true\n nullspace of the velocity block (A_uu\u00b7rotation = 0). The monolithic\n nullspace attached for the solve is NOT removed inside a fieldsplit/Schur\n iteration \u2014 the inner velocity KSP has no rotation nullspace, so it leaves\n an unconstrained rigid rotation in the velocity. Because the operator is\n blind to it, the true residual still converges to machine precision, but\n the rotation amplitude is PARTITION-DEPENDENT (the tangential velocity then\n differs serial-vs-parallel by ~0.1-1% while the physical / radial flow is\n partition-clean to round-off). Since the rotation is a genuine nullspace,\n removing it from the converged ``gvec`` does NOT change the residual and\n yields the same rotation-free solution on any decomposition. This is the\n velocity analogue of the constant-pressure gauge (see set_pressure_gauge).\n No-op when there are no velocity rotation modes.\n\n Why a post-solve projection rather than attaching the rotation nullspace\n to the fieldsplit velocity SUB-BLOCK so the inner KSP removes it in-solve:\n the in-solve route was implemented and MEASURED, and it does not fix the\n gauge. A ``DMSetNullSpaceConstructor(dm, 0, ...)`` (rotations-only, built\n with ``DMPlexCreateRigidBody``) DOES attach the rotation nullspace to the\n fieldsplit velocity block \u2014 verified: the velocity sub-block operator\n carries the 1 (2-D) / 3 (3-D) rotation modes after PC setup \u2014 yet the\n converged GLOBAL velocity still retained the full rotation gauge (rotation\n coefficient ~0.18, i.e. unchanged). Removing a nullspace gauge is a\n projection on the converged GLOBAL solution; attaching the nullspace to a\n sub-block operator only constrains the inner Krylov solves, and with\n ``fgmres`` + a variable (fieldsplit/Schur) preconditioner that reintroduces\n rotation, the assembled outer solution is not gauge-free. (PETSc also\n dispatches ``DMCreateSubDM`` nullspace constructors by SUB-DM-LOCAL field\n index, so a field-0 constructor needs a block-sniffing guard to avoid\n leaking onto the ``[p,h]`` Schur block \u2014 but even with that, the global\n gauge persists.) So the explicit post-solve projection is the correct AND\n necessary mechanism, not a stopgap: it is mathematically exact (the rotation\n is a true nullspace, so it does not change the residual) and robust to\n operator rebuild. Building only the rotation modes (zero in the pressure /\n multiplier blocks) keeps it from touching the pressure/multiplier gauge,\nwhich is handled by the monolithic nullspace.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6445, + "signature": "def _object_viewer(self):", + "parameters": [], + "returns": null, + "existing_docstring": "This will add specific information about this object to the generic class viewer\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_discretisation", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 6911, + "signature": "def _setup_discretisation(self, verbose=False):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Most of what is in the init phase that is not called by _setup_terms()\n", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_constrain_interior_multipliers_in_section", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7147, + "signature": "def _constrain_interior_multipliers_in_section(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Lossless, refined-safe boundary-only multiplier reduction.\n\n Block-constrained Stokes carries one Lagrange multiplier (field ``h``)\n per constraint as a full-domain field, but only its boundary trace is\n physical \u2014 the interior ``h`` DOFs are inert and merely inflate the\n ``[p,h]`` Schur block (~ndof/3 extra DOFs). Here we constrain every\n interior (off-constraint-boundary) ``h`` DOF DIRECTLY in the fine DM's\n local PetscSection, so they drop out of the GLOBAL system (smaller Schur\n block) while remaining present in the LOCAL vector at 0 (scatter-back\n stays size-correct, see below).\n\n This must run AFTER ``createClosureIndex`` (so the local section is\n finalised with the velocity Dirichlet constraints baked in AND the\n constraint-boundary closure is complete \u2014 the source of the earlier\n DMAddBoundary path's loss) and BEFORE any consumer of the GLOBAL section\n (the field-index fieldsplit grouping, the SNES, ``createFieldDecomposition``,\n the nullspace). Constraining in the section has no \"after section creation\"\n restriction, so it is also refined/FMG-safe \u2014 unlike the DMAddBoundary\n essential-field pin it replaces.\n\n Fine ``self.dm`` only: the coarse MG levels never carry an active ``h``\n field in the solve, and ``copyFields``/``copyDS`` copy discretisations and\n the DS (weak forms), not the section, so the velocity MG/FMG hierarchy and\n the field-index fieldsplit grouping are undisturbed (they only see the\n ``[p,h]`` block shrink).\n\n Note (scatter-back): PetscSection constraints remove DOFs from the GLOBAL\n section only; the LOCAL section still allocates them. The multiplier IS is\n built with ``unconstrained=False`` and (for a scalar ``h``, 1 dof/point)\n appends the local offset of every h-bearing point regardless of\n constraint, so the copy back into the full-domain ``h`` MeshVariable is\n size-correct without change.\n\n Why this is lossless (and why disabling it is NOT a \"more accurate\"\n reference). The interior ``h`` rows are the screening block alone,\n ``\u03b5 M_ii h_i + \u03b5 M_ib h_b = 0`` (no constraint or buoyancy source reaches\n the interior), so the interior multiplier is fully determined by the\n boundary trace, ``h_i = -M_ii^{-1} M_ib h_b`` \u2014 a *bounded* O(h_b)\n quantity that feeds back into the boundary row only at O(\u03b5). At \u03b5=1e-6 its\n effect on the physical solution is negligible: pinning ``h_i = 0`` instead\n moves a converged solve by ~1e-8 in velocity and ~1e-5 in topography\n (measured, 2-D annulus). So the pinned DOFs carry no physical signal.\n\n Disabling the reduction (``_reduce_interior_multiplier = False``) does NOT\n recover lost physics; it re-admits ~ndof/3 interior DOFs whose only\n coupling is the near-singular ``\u03b5 M_ii`` block, enlarging and\n ill-conditioning the ``[p,h]`` Schur complement. On a direct/well-converged\n solve the answer is essentially unchanged (lossless, as above); on an\n iterative grouped-Schur solve (e.g. ``snes_type=ksponly`` on the 3-D shell)\n the extra ill-conditioned DOFs degrade convergence and the solve can land\n on a different representative \u2014 which is the origin of the \"answer moves a\n few percent / parallel spread worsens when off\" behaviour. That is a\n conditioning effect, not evidence the knockout drops information. Keep it\n ON; it is the recommended, validated path.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_setup_region_ds", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7622, + "signature": "def _setup_region_ds(self):", + "parameters": [], + "returns": null, + "existing_docstring": "Register a trivial DS for the inactive region.\n\n After _setup_solver populates the default DS with Stokes weak forms,\n this creates an empty DS for cells in the inactive region. PETSc's\n DMGetCellDS dispatches per-cell: inactive cells get the empty DS\n (zero volume contributions), active cells get the default DS.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_ensure_local_field_index_sets", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7929, + "signature": "def _ensure_local_field_index_sets(self, clvec, local_section):", + "parameters": [], + "returns": null, + "existing_docstring": "Build (once) and cache the LOCAL index sets that decompose a parent-DM\n local vector into the per-field MeshVariable storage: velocity, pressure\n and any block-constraint multipliers.\n\n The index sets are state-independent given the section, so they are built\n on first use and reused until a rebuild resets ``_pressure_is`` to None\n (see ``_build``). Used both by the post-solve copy-back and the\n mid-solve callback scatter (``_scatter_global_to_fields``).\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_scatter_global_to_fields", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 7992, + "signature": "def _scatter_global_to_fields(self, gvec):", + "parameters": [], + "returns": null, + "existing_docstring": "Scatter the global iterate into the field MeshVariables, correctly on\n driven (non-zero Dirichlet) boundaries.\n\n The base-class scatter (per-field ``subdm.globalToLocal``) fills a field's\n interior and *zero*-Dirichlet DOFs, but NOT its non-zero Dirichlet (driven)\n boundary DOFs: those are not stored in the global vector \u2014 they are imposed\n on the *local* vector by ``DMPlexSNESComputeBoundaryFEM``. A callback that\n reads, e.g., the velocity on a lid would otherwise see stale values there\n (measured ~35% error in the Helmholtz smoother test).\n\n This override mirrors the post-solve copy-back: ``globalToLocal`` into a\n parent-DM local vec, apply the boundary FEM, then split per-field via the\n cached local index sets. Cache-invalidation tail matches the base method.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_gather_fields_to_global", + "kind": "method", + "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", + "line": 8041, + "signature": "def _gather_fields_to_global(self, gvec):", + "parameters": [], + "returns": null, + "existing_docstring": "Gather velocity / pressure / multiplier fields back into the global\n iterate via the per-field sub-DMs (the multi-field counterpart of the\nsingle-field base-class gather).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 62, + "signature": "def __init__( self,\n mesh: underworld3.discretisation.Mesh,\n fn: Union[float, int, sympy.Basic] ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 253, + "signature": "def __init__( self,\n mesh: underworld3.discretisation.Mesh,\n fn: Union[float, int, sympy.Basic] ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], "parent_class": null, - "is_public": true + "is_public": false }, { - "name": "UnstructuredSimplexBox", - "kind": "function", - "file": "src/underworld3/meshing/cartesian.py", - "line": 33, - "signature": "(minCoords: Tuple = (0.0, 0.0), maxCoords: Tuple = (1.0, 1.0), cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, regular: bool = False, filename = None, refinement = None, gmsh_verbosity = 0, units = None, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/cython/petsc_maths.pyx", + "line": 379, + "signature": "def __init__( self,\n mesh: underworld3.discretisation.Mesh,\n fn: Union[float, int, sympy.Basic],\n boundary: str ):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 257, + "signature": "(self, plex_or_meshfile, degree = 1, simplex = True, coordinate_system_type = None, qdegree = 2, markVertices = None, useRegions = None, useMultipleTags = None, filename = None, refinement = None, refinement_callback = None, coarsening = None, coarsening_callback = None, return_coords_to_bounds = None, boundaries = None, boundary_normals = None, name = None, units = None, verbose = False, *args, **kwargs)", "parameters": [ { - "name": "minCoords", - "type_hint": "Tuple", - "default": "(0.0, 0.0)", - "description": "" - }, - { - "name": "maxCoords", - "type_hint": "Tuple", - "default": "(1.0, 1.0)", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", + "name": "plex_or_meshfile", + "type_hint": null, + "default": null, "description": "" }, { "name": "degree", - "type_hint": "int", + "type_hint": null, "default": "1", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "regular", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "filename", + "name": "simplex", "type_hint": null, - "default": "None", + "default": "True", "description": "" }, { - "name": "refinement", + "name": "coordinate_system_type", "type_hint": null, "default": "None", "description": "" }, { - "name": "gmsh_verbosity", + "name": "qdegree", "type_hint": null, - "default": "0", + "default": "2", "description": "" }, { - "name": "units", + "name": "markVertices", "type_hint": null, "default": "None", "description": "" }, { - "name": "verbose", + "name": "useRegions", "type_hint": null, - "default": "False", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Create an unstructured simplex mesh on a rectangular box domain.\n\nGenerates a triangular (2D) or tetrahedral (3D) mesh using Gmsh,\nwith named boundary labels for applying boundary conditions.\n\nParameters\n----------\nminCoords : tuple of float\n Minimum corner coordinates ``(x_min, y_min)`` for 2D or\n ``(x_min, y_min, z_min)`` for 3D. Supports plain numbers\n (model units) or UWQuantity objects (auto-converted).\nmaxCoords : tuple of float\n Maximum corner coordinates ``(x_max, y_max)`` for 2D or\n ``(x_max, y_max, z_max)`` for 3D. Supports plain numbers\n or UWQuantity objects.\ncellSize : float\n Target mesh element size. Controls mesh density; smaller\n values produce finer meshes with more elements.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\n Use ``degree=1`` for linear elements, ``degree=2`` for quadratic.\nqdegree : int, default=2\n Quadrature degree for numerical integration. Should typically\n be at least ``2 * degree`` for accuracy.\nregular : bool, default=False\n If True, use transfinite meshing for a more structured layout.\n Currently only works for 2D meshes.\nfilename : str, optional\n Path to save the mesh file. If None, generates a unique name\n in the ``.meshes/`` directory based on mesh parameters.\nrefinement : int, optional\n Number of uniform refinement levels to apply after mesh\n generation. Each level approximately quadruples element count.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level. 0 is silent, higher values\n produce more diagnostic output.\nunits : str, optional\n **Deprecated**. Mesh coordinates are always in model reference\n units. This parameter is retained for backward compatibility.\nverbose : bool, default=False\n If True, print additional diagnostic information during\n mesh construction.\n\nReturns\n-------\nMesh\n An Underworld mesh object with the following boundaries defined:\n\n **2D boundaries** (accessible via ``mesh.boundaries``):\n\n - ``Bottom``: :math:`y = y_{min}` edge\n - ``Top``: :math:`y = y_{max}` edge\n - ``Right``: :math:`x = x_{max}` edge\n - ``Left``: :math:`x = x_{min}` edge\n\n **3D boundaries**:\n\n - ``Bottom``: :math:`z = z_{min}` face\n - ``Top``: :math:`z = z_{max}` face\n - ``Right``: :math:`x = x_{max}` face\n - ``Left``: :math:`x = x_{min}` face\n - ``Front``: :math:`y = y_{min}` face\n - ``Back``: :math:`y = y_{max}` face\n\nSee Also\n--------\nStructuredQuadBox : For quadrilateral/hexahedral meshes.\nBoxInternalBoundary : For box meshes with an internal interface.\n\nExamples\n--------\nCreate a 2D unit square mesh:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.UnstructuredSimplexBox(\n... minCoords=(0.0, 0.0),\n... maxCoords=(1.0, 1.0),\n... cellSize=0.1\n... )\n>>> mesh.dim\n2\n\nCreate a 3D box with finer resolution:\n\n>>> mesh3d = uw.meshing.UnstructuredSimplexBox(\n... minCoords=(0.0, 0.0, 0.0),\n... maxCoords=(2.0, 1.0, 1.0),\n... cellSize=0.05,\n... degree=2\n... )\n\nAccess boundary labels for boundary conditions:\n\n>>> mesh.boundaries.Bottom\n\n\nNotes\n-----\nMesh coordinates are always in non-dimensional (scaled) units (set via\n``uw.scaling.get_coefficients()``). If UWQuantity objects with\nphysical units are passed, they are automatically converted using\n``uw.scaling.non_dimensionalise()``.\n\nThe ``regular=True`` option produces a more structured mesh layout\nbut currently only works for 2D meshes.", - "harvested_comments": [ - "Enum is not quite natural but matches the above", - "Convert coordinates to non-dimensional units (handles UWQuantity objects)", - "Add Physical groups for boundaries", - "Add Physical groups", - "Generate Mesh" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "BoxInternalBoundary", - "kind": "function", - "file": "src/underworld3/meshing/cartesian.py", - "line": 360, - "signature": "(elementRes: Optional[Tuple[int, int, int]] = (8, 8, 8), zelementRes: Optional[Tuple[int, int]] = (4, 4), cellSize: float = 0.1, minCoords: Optional[Tuple[float, float, float]] = (0, 0, 0), maxCoords: Optional[Tuple[float, float, float]] = (1, 1, 1), zintCoord: float = 0.5, simplex: bool = False, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, units = None, verbose = False)", - "parameters": [ - { - "name": "elementRes", - "type_hint": "Optional[Tuple[int, int, int]]", - "default": "(8, 8, 8)", - "description": "" - }, - { - "name": "zelementRes", - "type_hint": "Optional[Tuple[int, int]]", - "default": "(4, 4)", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "minCoords", - "type_hint": "Optional[Tuple[float, float, float]]", - "default": "(0, 0, 0)", - "description": "" - }, - { - "name": "maxCoords", - "type_hint": "Optional[Tuple[float, float, float]]", - "default": "(1, 1, 1)", - "description": "" - }, - { - "name": "zintCoord", - "type_hint": "float", - "default": "0.5", - "description": "" - }, - { - "name": "simplex", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", + "default": "None", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "useMultipleTags", + "type_hint": null, + "default": "None", "description": "" }, { @@ -19583,908 +45481,747 @@ "description": "" }, { - "name": "gmsh_verbosity", + "name": "refinement_callback", "type_hint": null, - "default": "0", + "default": "None", "description": "" }, { - "name": "units", + "name": "coarsening", "type_hint": null, "default": "None", "description": "" }, { - "name": "verbose", + "name": "coarsening_callback", "type_hint": null, - "default": "False", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Create a box mesh with an internal horizontal boundary.\n\nGenerates a 2D or 3D mesh with an embedded internal boundary surface,\nuseful for problems with material interfaces, phase boundaries, or\nlayered domains that require flux calculations across the interface.\n\nParameters\n----------\nelementRes : tuple of int, default=(8, 8, 8)\n Number of elements in each direction ``(nx, ny)`` for 2D or\n ``(nx, ny, nz)`` for 3D. Used when ``simplex=False`` (structured).\nzelementRes : tuple of int, default=(4, 4)\n Number of elements ``(n_below, n_above)`` in the vertical direction\n below and above the internal boundary. Allows different resolution\n in each layer.\ncellSize : float, default=0.1\n Target element size for unstructured meshing (``simplex=True``).\n Ignored for structured meshes.\nminCoords : tuple of float, default=(0, 0, 0)\n Minimum corner coordinates. Length determines dimensionality:\n 2-tuple for 2D, 3-tuple for 3D.\nmaxCoords : tuple of float, default=(1, 1, 1)\n Maximum corner coordinates.\nzintCoord : float, default=0.5\n Vertical coordinate of the internal boundary surface.\n In 2D this is the y-coordinate; in 3D the z-coordinate.\nsimplex : bool, default=False\n If False, create a structured quadrilateral/hexahedral mesh.\n If True, create an unstructured triangular/tetrahedral mesh.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file. If None, auto-generates in ``.meshes/``.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nunits : str, optional\n Coordinate units for unit-aware arrays.\nverbose : bool, default=False\n Print diagnostic information during mesh construction.\n\nReturns\n-------\nMesh\n An Underworld mesh object with boundaries including an internal\n interface:\n\n **2D boundaries**:\n\n - ``Bottom``: :math:`y = y_{min}` edge\n - ``Top``: :math:`y = y_{max}` edge\n - ``Right``: :math:`x = x_{max}` edge\n - ``Left``: :math:`x = x_{min}` edge\n - ``Internal``: :math:`y = z_{int}` interface\n\n **3D boundaries**:\n\n - ``Bottom``: :math:`z = z_{min}` face\n - ``Top``: :math:`z = z_{max}` face\n - ``Right``: :math:`x = x_{max}` face\n - ``Left``: :math:`x = x_{min}` face\n - ``Front``: :math:`y = y_{min}` face\n - ``Back``: :math:`y = y_{max}` face\n - ``Internal``: :math:`z = z_{int}` interface\n\nSee Also\n--------\nUnstructuredSimplexBox : Box mesh without internal boundary.\nAnnulusInternalBoundary : Annular mesh with internal boundary.\n\nExamples\n--------\nCreate a 2D layered domain with an interface at y=0.5:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.BoxInternalBoundary(\n... minCoords=(0.0, 0.0),\n... maxCoords=(1.0, 1.0),\n... zintCoord=0.5,\n... elementRes=(16, 16),\n... zelementRes=(8, 8)\n... )\n\nAccess the internal boundary for flux calculations:\n\n>>> mesh.boundaries.Internal\n\n\nNotes\n-----\nThe internal boundary is useful for:\n\n- Calculating heat flux across a thermal boundary layer\n- Tracking mass flux between mantle and crust\n- Applying different material properties in each layer", - "harvested_comments": [ - "structuredQuadBoxIB", - "Add Physical groups for boundaries" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "StructuredQuadBox", - "kind": "function", - "file": "src/underworld3/meshing/cartesian.py", - "line": 898, - "signature": "(elementRes: Optional[Tuple[int, int, int]] = (16, 16), minCoords: Optional[Tuple[float, float, float]] = None, maxCoords: Optional[Tuple[float, float, float]] = None, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, units = None, verbose = False)", - "parameters": [ - { - "name": "elementRes", - "type_hint": "Optional[Tuple[int, int, int]]", - "default": "(16, 16)", - "description": "" - }, - { - "name": "minCoords", - "type_hint": "Optional[Tuple[float, float, float]]", "default": "None", "description": "" }, { - "name": "maxCoords", - "type_hint": "Optional[Tuple[float, float, float]]", + "name": "return_coords_to_bounds", + "type_hint": null, "default": "None", "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "boundaries", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "boundary_normals", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "filename", + "name": "name", "type_hint": null, "default": "None", "description": "" }, { - "name": "refinement", + "name": "units", "type_hint": null, "default": "None", "description": "" }, { - "name": "gmsh_verbosity", + "name": "verbose", "type_hint": null, - "default": "0", + "default": "False", "description": "" }, { - "name": "units", + "name": "*args", "type_hint": null, - "default": "None", + "default": null, "description": "" }, { - "name": "verbose", + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create a structured quadrilateral or hexahedral box mesh.\n\nGenerates a mesh with regular rectangular (2D) or brick (3D) elements\nusing transfinite meshing. Provides precise control over element count\nin each direction.\n\nParameters\n----------\nelementRes : tuple of int, default=(16, 16)\n Number of elements in each direction. Use ``(nx, ny)`` for 2D\n or ``(nx, ny, nz)`` for 3D. This tuple also determines the\n mesh dimensionality.\nminCoords : tuple of float, optional\n Minimum corner coordinates. Defaults to ``(0.0, 0.0)`` for 2D\n or ``(0.0, 0.0, 0.0)`` for 3D based on ``elementRes`` length.\n Supports plain numbers or UWQuantity objects.\nmaxCoords : tuple of float, optional\n Maximum corner coordinates. Defaults to ``(1.0, 1.0)`` for 2D\n or ``(1.0, 1.0, 1.0)`` for 3D. Supports UWQuantity objects.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\n Use ``degree=1`` for bilinear/trilinear elements,\n ``degree=2`` for biquadratic/triquadratic.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file. If None, auto-generates in ``.meshes/``.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nunits : str, optional\n **Deprecated**. Mesh coordinates are always in model reference units.\nverbose : bool, default=False\n Print diagnostic information during mesh construction.\n\nReturns\n-------\nMesh\n An Underworld mesh object with structured elements and boundaries:\n\n **2D boundaries** (same as UnstructuredSimplexBox):\n\n - ``Bottom``: :math:`y = y_{min}` edge\n - ``Top``: :math:`y = y_{max}` edge\n - ``Right``: :math:`x = x_{max}` edge\n - ``Left``: :math:`x = x_{min}` edge\n\n **3D boundaries**:\n\n - ``Bottom``: :math:`z = z_{min}` face\n - ``Top``: :math:`z = z_{max}` face\n - ``Right``: :math:`x = x_{max}` face\n - ``Left``: :math:`x = x_{min}` face\n - ``Front``: :math:`y = y_{min}` face\n - ``Back``: :math:`y = y_{max}` face\n\nSee Also\n--------\nUnstructuredSimplexBox : For triangular/tetrahedral meshes.\nBoxInternalBoundary : For box meshes with an internal interface.\n\nExamples\n--------\nCreate a 2D structured mesh with 32x32 elements:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.StructuredQuadBox(\n... elementRes=(32, 32),\n... minCoords=(0.0, 0.0),\n... maxCoords=(1.0, 1.0)\n... )\n\nCreate a 3D mesh (note the 3-element tuple):\n\n>>> mesh3d = uw.meshing.StructuredQuadBox(\n... elementRes=(16, 16, 8),\n... maxCoords=(2.0, 2.0, 1.0)\n... )\n\nNotes\n-----\nStructured meshes have predictable element layouts which can be\nadvantageous for:\n\n- Consistent interpolation behaviour\n- Benchmark problems with known analytical solutions\n- Simpler mesh-to-mesh comparisons in convergence studies\n\nThe mesh dimensionality is determined by the length of ``elementRes``:\n2-tuple creates a 2D mesh, 3-tuple creates a 3D mesh.", + "existing_docstring": null, "harvested_comments": [ - "boundaries = {\"Bottom\": 1, \"Top\": 2, \"Right\": 3, \"Left\": 4, \"Front\": 5, \"Back\": 6}", - "Enum is not quite natural but matches the above", - "Convert coordinates to non-dimensional units (handles UWQuantity objects)", - "Detect units from UWQuantity inputs (if not explicitly specified)", - "Try to detect units from maxCoords (most likely to have units)" + "Coordinate units come from the model (not a user parameter):", + "the model owns the unit system so all meshes and variables agree.", + "Deprecated 2026-07 (WA-23/WC-11, units-family ruling D7): the", + "`units=` kwarg was always ignored in favour of the model units;", + "now it says so." ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Mesh", + "is_public": false }, { - "name": "points", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 116, - "signature": "(self) -> Optional[np.ndarray]", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 183, + "signature": "def __init__(self, mesh, eta_A=1.0, eta_B=1.0e6, x_c=0.5, n=1):", "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(N, 3) array of surface points.", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "FaultSurface", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "points", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 121, - "signature": "(self, value: np.ndarray)", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 34, + "signature": "(self, mesh, name: str = 'default')", "parameters": [ { - "name": "value", - "type_hint": "np.ndarray", + "name": "mesh", + "type_hint": null, "default": null, "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": "'default'", + "description": "" } ], "returns": null, - "existing_docstring": "Set points and invalidate cached data.", + "existing_docstring": null, "harvested_comments": [ - "Invalidate derived data" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "triangles", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 137, - "signature": "(self) -> Optional[np.ndarray]", - "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(M, 3) array of triangle vertex indices.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "normals", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 142, - "signature": "(self) -> Optional[np.ndarray]", - "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(M, 3) array of face normals.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "pv_mesh", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 147, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "PyVista PolyData mesh (None if not triangulated or pyvista unavailable).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "is_triangulated", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 152, - "signature": "(self) -> bool", - "parameters": [], - "returns": "bool", - "existing_docstring": "Whether the surface has been triangulated.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "n_points", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 157, - "signature": "(self) -> int", - "parameters": [], - "returns": "int", - "existing_docstring": "Number of points in the surface.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "Stores CachedDMInterpolationInfo" ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "n_triangles", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 162, - "signature": "(self) -> int", - "parameters": [], - "returns": "int", - "existing_docstring": "Number of triangles in the surface.", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "FaultSurface", - "is_public": true + "parent_class": "DMInterpolationCache", + "is_public": false }, { - "name": "from_vtk", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 167, - "signature": "(cls, filename: str, name: str = None) -> 'FaultSurface'", + "file": "src/underworld3/function/expressions.py", + "line": 830, + "signature": "(self, name, sym = None, description = 'No description provided', value = None, units = None, **kwargs)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "name", + "type_hint": null, "default": null, "description": "" }, { - "name": "name", - "type_hint": "str", + "name": "sym", + "type_hint": null, "default": "None", "description": "" - } - ], - "returns": "'FaultSurface'", - "existing_docstring": "Load fault surface from VTK file.\n\nArgs:\n filename: Path to VTK file (.vtk or .vtp)\n name: Name for the fault. If None, uses filename stem.\n\nReturns:\n FaultSurface: Loaded fault surface with triangulation and normals\n\nRaises:\n FileNotFoundError: If file doesn't exist\n ImportError: If pyvista not available", - "harvested_comments": [ - "Load the VTK file", - "Extract triangles from faces", - "VTK faces format: [n_verts, v0, v1, v2, n_verts, v0, v1, v2, ...]", - "Reshape to extract triangles (assumes all triangles)", - "Extract or compute normals" - ], - "status": "complete", - "needs": [], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "triangulate", - "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 215, - "signature": "(self, offset: float = 0.01) -> None", - "parameters": [ + }, { - "name": "offset", - "type_hint": "float", - "default": "0.01", + "name": "description", + "type_hint": null, + "default": "'No description provided'", "description": "" - } - ], - "returns": "None", - "existing_docstring": "Triangulate point cloud using pyvista delaunay_2d.\n\nThis creates a triangulated surface from the point cloud by projecting\npoints onto a best-fit plane, performing 2D Delaunay triangulation,\nand mapping back to 3D.\n\nArgs:\n offset: Height offset for delaunay_2d (controls curvature tolerance).\n Larger values allow more curved surfaces.\n\nRaises:\n ImportError: If pyvista not available\n ValueError: If points too sparse for triangulation (< 3 points)\n RuntimeError: If triangulation fails", - "harvested_comments": [ - "Check for degenerate cases (all points nearly collinear)", - "Compute bounding box extent", - "If smallest extent is negligible compared to largest, points may be collinear", - "Create PolyData from points and triangulate", - "This runs on all ranks redundantly (pyvista doesn't work in parallel)" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "compute_normals", - "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 273, - "signature": "(self, consistent_normals: bool = True) -> None", - "parameters": [ + }, { - "name": "consistent_normals", - "type_hint": "bool", - "default": "True", + "name": "value", + "type_hint": null, + "default": "None", "description": "" - } - ], - "returns": "None", - "existing_docstring": "Recompute face normals for triangulated surface.\n\nArgs:\n consistent_normals: If True, attempt to make normals consistently oriented", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "FaultSurface", - "is_public": true - }, - { - "name": "flip_normals", - "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 292, - "signature": "(self) -> None", - "parameters": [], - "returns": "None", - "existing_docstring": "Flip the direction of all face normals.", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Legacy parameter", + "Units for wrapping the value", + "Handle legacy 'value' parameter", + "If units are provided and sym is a plain numeric value, wrap it in UWQuantity", + "Don't wrap if sym is already a UWQuantity, UWexpression, or SymPy expression" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "FaultSurface", - "is_public": true + "parent_class": "UWexpression", + "is_public": false }, { - "name": "to_vtk", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 299, - "signature": "(self, filename: str) -> None", + "file": "src/underworld3/function/expressions.py", + "line": 1768, + "signature": "(self, expr, *args, **kwargs)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Export triangulated surface to VTK file.\n\nArgs:\n filename: Output path (.vtk or .vtp)\n\nRaises:\n RuntimeError: If surface not triangulated\n ImportError: If pyvista not available", - "harvested_comments": [ - "Ensure we have a pyvista mesh", - "Reconstruct from arrays" - ], - "status": "partial", + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "FaultSurface", - "is_public": true + "parent_class": "UWDerivativeExpression", + "is_public": false }, { - "name": "build_kdtree", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 328, - "signature": "(self) -> 'uw.kdtree.KDTree'", + "file": "src/underworld3/materials.py", + "line": 150, + "signature": "(self)", "parameters": [], - "returns": "'uw.kdtree.KDTree'", - "existing_docstring": "Build KDTree for nearest-neighbor queries on face centers.\n\nReturns:\n KDTree built from triangle centroids\n\nRaises:\n RuntimeError: If surface not triangulated", + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Compute triangle centroids" + "region_id -> material_name", + "Material change callbacks" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "FaultSurface", - "is_public": true + "parent_class": "MaterialRegistry", + "is_public": false }, { - "name": "face_centers", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 350, - "signature": "(self) -> np.ndarray", - "parameters": [], - "returns": "np.ndarray", - "existing_docstring": "(M, 3) array of triangle centroids.", - "harvested_comments": [ - "Compute manually from triangles" + "name": "__init__", + "kind": "method", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 63, + "signature": "(self, mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "minimal", + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "FaultSurface", - "is_public": true + "parent_class": "mesh_vector_calculus", + "is_public": false }, { - "name": "add", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 407, - "signature": "(self, fault: FaultSurface) -> None", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 290, + "signature": "(self, mesh)", "parameters": [ { - "name": "fault", - "type_hint": "FaultSurface", + "name": "mesh", + "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Add a fault surface to the collection.\n\nArgs:\n fault: FaultSurface to add\n\nRaises:\n ValueError: If fault with same name already exists", - "harvested_comments": [], - "status": "partial", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "NATIVE coordinate systems deprecated, always warn" + ], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "FaultCollection", - "is_public": true + "parent_class": "mesh_vector_calculus_cylindrical", + "is_public": false }, { - "name": "add_from_vtk", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 423, - "signature": "(self, filename: str, name: str = None) -> FaultSurface", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 616, + "signature": "(self, mesh)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "mesh", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": "None", - "description": "" } ], - "returns": "FaultSurface", - "existing_docstring": "Load and add a fault from VTK file.\n\nArgs:\n filename: Path to VTK file\n name: Name for the fault. If None, uses filename stem.\n\nReturns:\n The loaded FaultSurface", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "FaultCollection", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "mesh_vector_calculus_spherical", + "is_public": false }, { - "name": "remove", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 437, - "signature": "(self, name: str) -> FaultSurface", + "file": "src/underworld3/maths/vector_calculus.py", + "line": 807, + "signature": "(self, mesh)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "mesh", + "type_hint": null, "default": null, "description": "" } ], - "returns": "FaultSurface", - "existing_docstring": "Remove and return a fault from the collection.\n\nArgs:\n name: Name of fault to remove\n\nReturns:\n The removed FaultSurface\n\nRaises:\n KeyError: If fault not found", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "FaultCollection", - "is_public": true - }, - { - "name": "names", - "kind": "property", - "file": "src/underworld3/meshing/faults.py", - "line": 464, - "signature": "(self) -> List[str]", - "parameters": [], - "returns": "List[str]", - "existing_docstring": "List of fault names.", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "FaultCollection", - "is_public": true + "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "is_public": false }, { - "name": "compute_distance_field", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 468, - "signature": "(self, mesh: 'Mesh', distance_var: 'MeshVariable' = None, variable_name: str = 'fault_distance') -> 'MeshVariable'", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 63, + "signature": "(self, mesh, label, kind)", "parameters": [ { "name": "mesh", - "type_hint": "'Mesh'", + "type_hint": null, "default": null, "description": "" }, { - "name": "distance_var", - "type_hint": "'MeshVariable'", - "default": "None", + "name": "label", + "type_hint": null, + "default": null, "description": "" }, { - "name": "variable_name", - "type_hint": "str", - "default": "'fault_distance'", + "name": "kind", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Compute minimum distance from mesh points to any fault surface.\n\nUses pyvista's compute_implicit_distance for accurate signed distance\ncomputation. The returned field contains the absolute distance to the\nnearest fault surface at each mesh point.\n\nArgs:\n mesh: The mesh to compute distances on\n distance_var: Optional existing MeshVariable to store results.\n If None, creates a new variable.\n variable_name: Name for new variable if distance_var is None\n\nReturns:\n MeshVariable with distance values (scalar, 1 component)\n\nRaises:\n ValueError: If collection is empty or no faults are triangulated\n ImportError: If pyvista not available", + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Check all faults are triangulated", - "Create or validate output variable", - "Get mesh coordinates and create pyvista point cloud", - "(avoids visualisation module which initializes trame)", - "Initialize with large distance" + "reference_facets: (nf, cdim, cdim) \u2014 line segments (2D) / triangles", + "(3D) of the surface, captured from a FIXED reference, for the `facet`", + "nearest-point restore on non-analytic surfaces.", + "radius == 0 is legitimate: a *solid* sphere/annulus registers its", + "inner (\"Lower\") boundary at radius 0 (the centre point). Reject" ], - "status": "complete", - "needs": [], - "parent_class": "FaultCollection", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "BoundingSurface", + "is_public": false }, { - "name": "transfer_normals", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 535, - "signature": "(self, mesh: 'Mesh', coords: np.ndarray = None, normal_var: 'MeshVariable' = None, variable_name: str = 'fault_normals') -> 'MeshVariable'", + "file": "src/underworld3/mpi.py", + "line": 387, + "signature": "(self, pattern = 'collective', returnobj = None)", "parameters": [ { - "name": "mesh", - "type_hint": "'Mesh'", - "default": null, - "description": "" - }, - { - "name": "coords", - "type_hint": "np.ndarray", - "default": "None", + "name": "pattern", + "type_hint": null, + "default": "'collective'", "description": "" }, { - "name": "normal_var", - "type_hint": "'MeshVariable'", + "name": "returnobj", + "type_hint": null, "default": "None", "description": "" - }, - { - "name": "variable_name", - "type_hint": "str", - "default": "'fault_normals'", - "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Transfer fault normals to mesh points via nearest-neighbor lookup.\n\nFor each mesh point, finds the closest fault face (from any fault in\nthe collection) and copies that face's normal vector.\n\nArgs:\n mesh: The mesh to transfer normals to\n coords: Optional coordinates to query. If None, uses mesh.X.coords\n normal_var: Optional existing MeshVariable to store results.\n If None, creates a new variable.\n variable_name: Name for new variable if normal_var is None\n\nReturns:\n MeshVariable with normal vectors (3 components)\n\nRaises:\n ValueError: If collection is empty or no faults are triangulated", + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "call_pattern", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/parameters.py", + "line": 141, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Check all faults are triangulated", - "Get query coordinates", - "Handle UnitAwareArray by extracting raw values", - "Create or validate output variable", - "Build combined arrays of all fault face centers and normals" + "Parameter change callbacks", + "Parameter change history" ], - "status": "complete", - "needs": [], - "parent_class": "FaultCollection", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ParameterRegistry", + "is_public": false }, { - "name": "create_weakness_function", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 606, - "signature": "(self, distance_var: 'MeshVariable', fault_width: float, eta_weak: float = 0.01, eta_background: float = 1.0) -> sympy.Expr", + "file": "src/underworld3/scaling/_utils.py", + "line": 36, + "signature": "(self, mapping = (), **kwargs)", "parameters": [ { - "name": "distance_var", - "type_hint": "'MeshVariable'", - "default": null, + "name": "mapping", + "type_hint": null, + "default": "()", "description": "" }, { - "name": "fault_width", - "type_hint": "float", + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "eta_weak", - "type_hint": "float", - "default": "0.01", - "description": "" - }, - { - "name": "eta_background", - "type_hint": "float", - "default": "1.0", - "description": "" } ], - "returns": "sympy.Expr", - "existing_docstring": "Create Piecewise viscosity function for fault weakness.\n\nCreates a sympy Piecewise expression that gives:\n- eta_weak when distance < fault_width\n- eta_background otherwise\n\nThis can be used directly with TransverseIsotropicFlowModel.Parameters.eta_1\nfor creating anisotropic weakness along fault zones.\n\nArgs:\n distance_var: MeshVariable containing fault distances\n fault_width: Width of the weak zone around faults\n eta_weak: Viscosity within fault zone (default 0.01)\n eta_background: Viscosity outside fault zone (default 1.0)\n\nReturns:\n sympy.Piecewise expression for use in constitutive models\n\nExample:\n >>> eta_1 = faults.create_weakness_function(\n ... fault_distance,\n ... fault_width=mesh.get_min_radius() * 5,\n ... eta_weak=0.01,\n ... )\n >>> stokes.constitutive_model.Parameters.eta_1 = eta_1", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "FaultCollection", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "TransformedDict", + "is_public": false }, { - "name": "RegionalSphericalBox", - "kind": "function", - "file": "src/underworld3/meshing/geographic.py", - "line": 29, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, SWcorner = [-45, -45], NEcorner = [+45, +45], numElementsLon: int = 5, numElementsLat: int = 5, numElementsDepth: int = 5, degree: int = 1, qdegree: int = 2, simplex: bool = False, filename = None, refinement = None, coarsening = None, gmsh_verbosity = 0, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 137, + "signature": "(self, name, swarm, size = None, vtype = None, dtype = float, proxy_degree = 1, proxy_continuous = True, _register = True, _proxy = True, _nn_proxy = False, varsymbol = None, rebuild_on_cycle = True, units = None, units_backend = None)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", + "name": "name", + "type_hint": null, + "default": null, "description": "" }, { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", + "name": "swarm", + "type_hint": null, + "default": null, "description": "" }, { - "name": "SWcorner", + "name": "size", "type_hint": null, - "default": "[-45, -45]", + "default": "None", "description": "" }, { - "name": "NEcorner", + "name": "vtype", "type_hint": null, - "default": "[+45, +45]", + "default": "None", "description": "" }, { - "name": "numElementsLon", - "type_hint": "int", - "default": "5", + "name": "dtype", + "type_hint": null, + "default": "float", "description": "" }, { - "name": "numElementsLat", - "type_hint": "int", - "default": "5", + "name": "proxy_degree", + "type_hint": null, + "default": "1", "description": "" }, { - "name": "numElementsDepth", - "type_hint": "int", - "default": "5", + "name": "proxy_continuous", + "type_hint": null, + "default": "True", "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "_register", + "type_hint": null, + "default": "True", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "_proxy", + "type_hint": null, + "default": "True", "description": "" }, { - "name": "simplex", - "type_hint": "bool", + "name": "_nn_proxy", + "type_hint": null, "default": "False", "description": "" }, { - "name": "filename", + "name": "varsymbol", "type_hint": null, "default": "None", "description": "" }, { - "name": "refinement", + "name": "rebuild_on_cycle", "type_hint": null, - "default": "None", + "default": "True", "description": "" }, { - "name": "coarsening", + "name": "units", "type_hint": null, "default": "None", "description": "" }, { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "verbose", + "name": "units_backend", "type_hint": null, - "default": "False", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Create a regional spherical box mesh (cubed-sphere section).\n\nGenerates a 3D structured mesh for a regional section of a spherical shell,\nusing a cubed-sphere projection. The domain is defined by corner coordinates\nin degrees (longitude, latitude) and radial bounds.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the spherical shell.\nradiusInner : float, default=0.547\n Inner radius of the spherical shell.\nSWcorner : list of float, default=[-45, -45]\n Southwest corner as [longitude, latitude] in degrees.\nNEcorner : list of float, default=[+45, +45]\n Northeast corner as [longitude, latitude] in degrees.\nnumElementsLon : int, default=5\n Number of elements in the longitude direction.\nnumElementsLat : int, default=5\n Number of elements in the latitude direction.\nnumElementsDepth : int, default=5\n Number of elements in the radial (depth) direction.\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nsimplex : bool, default=False\n If True, use tetrahedral elements; if False, use hexahedral.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ncoarsening : int, optional\n Number of coarsening levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n - ``North``: Northern boundary at :math:`\\phi = \\phi_{max}`\n - ``South``: Southern boundary at :math:`\\phi = \\phi_{min}`\n - ``East``: Eastern boundary at :math:`\\lambda = \\lambda_{max}`\n - ``West``: Western boundary at :math:`\\lambda = \\lambda_{min}`\n\n The mesh uses a SPHERICAL coordinate system and includes a refinement\n callback that snaps boundary nodes to true spherical geometry.\n\nSee Also\n--------\nCubedSphere : Full cubed-sphere mesh.\nRegionalGeographicBox : Geographic mesh with ellipsoidal geometry.\nSphericalShell : Unstructured spherical shell.\n\nExamples\n--------\nCreate a regional mesh for the Australian region:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.RegionalSphericalBox(\n... radiusOuter=1.0,\n... radiusInner=0.9,\n... SWcorner=[110, -45],\n... NEcorner=[155, -10],\n... numElementsLon=10,\n... numElementsLat=8,\n... numElementsDepth=5\n... )\n\nNotes\n-----\nThis mesh uses a cubed-sphere projection, which provides more uniform\nelement sizes than a latitude-longitude grid. The structured mesh is\nsuitable for regional mantle convection models where boundary-aligned\nelements are beneficial.\n\nThe coordinate system provides unit vectors via ``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "existing_docstring": null, "harvested_comments": [ - "lat_south = np.radians(centralLatitude - latitudeExtent/2)", - "lat_north = np.radians(centralLatitude + latitudeExtent/2)", - "ss = min(longitudeExtent / 90, 1.99) * np.cos(lat_south)/np.cos(np.pi/4)", - "sn = min(longitudeExtent / 90, 1.99) * np.cos(lat_north)/np.cos(np.pi/4)", - "t = min(latitudeExtent / 90, 1.99)" + "only needed if MATRIX type", + "Note: on a cd-1 mesh (dim < cdim, e.g. SphericalManifold),", + "vector fields are stored with ``cdim`` components in the", + "embedded coordinate space (tangent-constrained 3-vectors", + "on a 2-manifold) and the SwarmVariable coord cache is" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": false }, { - "name": "RegionalGeographicBox", - "kind": "function", - "file": "src/underworld3/meshing/geographic.py", - "line": 410, - "signature": "(lon_range: Tuple[float, float] = (135.0, 140.0), lat_range: Tuple[float, float] = (-35.0, -30.0), depth_range: Tuple[float, float] = (0.0, 400.0), ellipsoid = 'WGS84', numElements: Tuple[int, int, int] = (10, 10, 10), degree: int = 1, qdegree: int = 2, simplex: bool = True, filename: Optional[str] = None, refinement: Optional[int] = None, coarsening: Optional[int] = None, gmsh_verbosity: int = 0, verbose: bool = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2181, + "signature": "(self, name, swarm, indices = 1, proxy_degree = 1, proxy_continuous = True, update_type = 0, npoints = 5, radius = 0.5, npoints_bc = 2, ind_bc = None, varsymbol = None)", "parameters": [ { - "name": "lon_range", - "type_hint": "Tuple[float, float]", - "default": "(135.0, 140.0)", - "description": "" - }, - { - "name": "lat_range", - "type_hint": "Tuple[float, float]", - "default": "(-35.0, -30.0)", - "description": "" - }, - { - "name": "depth_range", - "type_hint": "Tuple[float, float]", - "default": "(0.0, 400.0)", - "description": "" - }, - { - "name": "ellipsoid", + "name": "name", "type_hint": null, - "default": "'WGS84'", + "default": null, "description": "" }, { - "name": "numElements", - "type_hint": "Tuple[int, int, int]", - "default": "(10, 10, 10)", + "name": "swarm", + "type_hint": null, + "default": null, "description": "" }, { - "name": "degree", - "type_hint": "int", + "name": "indices", + "type_hint": null, "default": "1", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "proxy_degree", + "type_hint": null, + "default": "1", "description": "" }, { - "name": "simplex", - "type_hint": "bool", + "name": "proxy_continuous", + "type_hint": null, "default": "True", "description": "" }, { - "name": "filename", - "type_hint": "Optional[str]", - "default": "None", + "name": "update_type", + "type_hint": null, + "default": "0", "description": "" }, { - "name": "refinement", - "type_hint": "Optional[int]", - "default": "None", + "name": "npoints", + "type_hint": null, + "default": "5", "description": "" }, { - "name": "coarsening", - "type_hint": "Optional[int]", - "default": "None", + "name": "radius", + "type_hint": null, + "default": "0.5", "description": "" }, { - "name": "gmsh_verbosity", - "type_hint": "int", - "default": "0", + "name": "npoints_bc", + "type_hint": null, + "default": "2", "description": "" }, { - "name": "verbose", - "type_hint": "bool", - "default": "False", + "name": "ind_bc", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "varsymbol", + "type_hint": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Create a regional geographic mesh with ellipsoidal geometry.\n\nThis function creates a structured 3D mesh in geographic coordinates\n(longitude, latitude, depth) on an ellipsoidal planet. The mesh uses\ngeodetic latitude (perpendicular to ellipsoid surface) and measures\ndepth below the reference ellipsoid surface.\n\nParameters\n----------\nlon_range : tuple of float, optional\n Longitude range in degrees East (lon_min, lon_max).\n Default: (135.0, 140.0) for southeastern Australia.\nlat_range : tuple of float, optional\n Latitude range in degrees North (lat_min, lat_max).\n Geodetic latitude (perpendicular to ellipsoid).\n Default: (-35.0, -30.0) for southeastern Australia.\ndepth_range : tuple of float, optional\n Depth range in km below ellipsoid surface (depth_min, depth_max).\n Positive downward. depth_min=0 means surface.\n Default: (0.0, 400.0) for 0-400 km depth.\nellipsoid : str, tuple, or bool, optional\n Ellipsoid specification:\n - str: Name from ELLIPSOIDS dict ('WGS84', 'Mars', 'Moon', 'Venus', 'sphere')\n - tuple: (semi_major_axis_km, semi_minor_axis_km) for custom ellipsoid\n - True: Use WGS84 (default)\n - False or 'sphere': Use perfect sphere with Earth mean radius\n Default: 'WGS84'\nnumElements : tuple of int, optional\n Number of elements in (lon, lat, depth) directions.\n Default: (10, 10, 10)\ndegree : int, optional\n Polynomial degree for finite elements (1=linear, 2=quadratic).\n Default: 1\nqdegree : int, optional\n Quadrature degree for numerical integration.\n Default: 2\nsimplex : bool, optional\n If True, use tetrahedral elements. If False, use hexahedral elements.\n Default: True\nfilename : str, optional\n Path to save generated mesh file. If None, uses automatic naming.\n Default: None\nrefinement : int, optional\n Number of uniform refinement steps to apply.\n Default: None\ncoarsening : int, optional\n Number of coarsening steps to apply.\n Default: None\ngmsh_verbosity : int, optional\n Gmsh output verbosity level (0=quiet, 5=very verbose).\n Default: 0\nverbose : bool, optional\n If True, print mesh generation details.\n Default: False\n\nReturns\n-------\nMesh\n Underworld3 mesh object with GEOGRAPHIC coordinate system.\n Access geographic coordinates via mesh.geo:\n - mesh.geo.lon, mesh.geo.lat, mesh.geo.depth (data arrays)\n - mesh.geo[:] for symbolic coordinates (\u03bb_lon, \u03bb_lat, \u03bb_d)\n - mesh.geo.unit_east, mesh.geo.unit_north, mesh.geo.unit_down (basis vectors)\n\nExamples\n--------\n# Create mesh for southeastern Australia, 0-400 km depth\nmesh = uw.meshing.RegionalGeographicBox(\n lon_range=(135, 140),\n lat_range=(-35, -30),\n depth_range=(0, 400),\n ellipsoid='WGS84',\n numElements=(20, 20, 10),\n)\n\n# Access geographic coordinates\nlon = mesh.geo.lon # Longitude array (degrees East)\nlat = mesh.geo.lat # Latitude array (degrees North)\ndepth = mesh.geo.depth # Depth array (km below surface)\n\n# Use in equations\n\u03bb_lon, \u03bb_lat, \u03bb_d = mesh.geo[:]\nT = 1600 - 0.5 * \u03bb_d # Temperature decreasing with depth\n\n# Basis vectors for boundary conditions\nv_surface = 0 * mesh.geo.unit_up # No vertical flow at surface\nv_bottom = 10 * mesh.geo.unit_down # Downward flow at bottom\n\n# Mars example\nmesh_mars = uw.meshing.RegionalGeographicBox(\n lon_range=(0, 45),\n lat_range=(-22.5, 22.5),\n depth_range=(0, 200),\n ellipsoid='Mars',\n numElements=(15, 15, 8),\n)\n\nNotes\n-----\n- Uses geodetic latitude (GPS/map standard), not geocentric latitude\n- Depth is measured from reference ellipsoid surface, not from center\n- mesh.R provides spherical coordinates (r, \u03b8, \u03c6) for backward compatibility\n- mesh.geo provides geographic coordinates (lon, lat, depth) with ellipsoid geometry\n- Right-handed coordinate system: WE \u00d7 SN = down", + "existing_docstring": null, "harvested_comments": [ - "Create mesh for southeastern Australia, 0-400 km depth", - "Access geographic coordinates", - "Longitude array (degrees East)", - "Latitude array (degrees North)", - "Depth array (km below surface)" + "**2 # changed to radius", + "These are the things we require of the generic swarm variable type", + "The indices variable defines how many \"level set\" maps we create as components in the proxy variable", + "Initialize lazy evaluation state", + "Proxy variables need initial update" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "IndexSwarmVariable", + "is_public": false }, { - "name": "SegmentedSphericalSurface2D", - "kind": "function", - "file": "src/underworld3/meshing/segmented.py", - "line": 30, - "signature": "(radius: float = 1.0, cellSize: float = 0.05, numSegments: int = 6, degree: int = 1, qdegree: int = 2, filename = None, gmsh_verbosity = 0, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2659, + "signature": "(self, mesh, recycle_rate = 0, verbose = False, clip_to_mesh = True)", "parameters": [ { - "name": "radius", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.05", - "description": "" - }, - { - "name": "numSegments", - "type_hint": "int", - "default": "6", + "name": "mesh", + "type_hint": null, + "default": null, "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "recycle_rate", + "type_hint": null, + "default": "0", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "filename", + "name": "clip_to_mesh", "type_hint": null, - "default": "None", + "default": "True", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Particle recycling (streak swarms) was excised in 2026-07: the", + "machinery had been broken (NameError) and untested for some time", + "(audit finding SWARM-08). Refuse rather than crash later.", + "Store reference to model instead of direct mesh reference", + "This enables dynamic mesh handover while maintaining access to mesh services" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 5091, + "signature": "(self, trackedVariable: uw.discretisation.MeshVariable, verbose = False)", + "parameters": [ { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", + "name": "trackedVariable", + "type_hint": "uw.discretisation.MeshVariable", + "default": null, "description": "" }, { @@ -20495,1709 +46232,1424 @@ } ], "returns": null, - "existing_docstring": "Create a 2D spherical surface mesh using orange-peel segmentation.\n\nGenerates a mesh on the surface of a sphere by dividing it into\nlongitudinal segments (like an orange peel), with coordinates\nconverted to :math:`(\\lambda, \\phi)` (longitude, latitude).\n\nParameters\n----------\nradius : float, default=1.0\n Radius of the sphere.\ncellSize : float, default=0.05\n Target mesh element size.\nnumSegments : int, default=6\n Number of longitudinal segments (orange-peel divisions).\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 2D surface mesh with point boundaries:\n\n - ``NPole``: North pole point\n - ``SPole``: South pole point\n - ``Poles``: Both poles combined\n\n Uses SPHERE_SURFACE_NATIVE coordinate system with\n :math:`(\\lambda, \\phi)` coordinates.\n\nSee Also\n--------\nSegmentedSphericalShell : 3D segmented spherical shell.\nSphericalShell : Standard unstructured spherical shell.\n\nWarnings\n--------\nThis mesh uses PETSc periodicity features that may be unreliable.\nConsider using alternative mesh types for production work.\n\nNotes\n-----\nThe mesh is constructed by creating curved triangular segments from\npole to pole, then converting the Cartesian coordinates to longitude-\nlatitude representation. Periodicity is set up to handle the :math:`\\pm\\pi`\nlongitude wrap-around.", + "existing_docstring": null, "harvested_comments": [ - "Mesh like an orange", - "Curve loops:", - "Add some physical labels etc.", - "Generate Mesh", - "xyz coordinates of the mesh" + "Keyword-explicit: Swarm.__init__ takes recycle_rate as its second", + "positional parameter, so a positional `verbose` here used to land", + "in recycle_rate and be silently discarded (SWARM-11).", + "The launch point location", + "The launch point index" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "NodalPointSwarm", + "is_public": false }, { - "name": "SegmentedSphericalShell", - "kind": "function", - "file": "src/underworld3/meshing/segmented.py", - "line": 238, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, numSegments: int = 6, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, coordinatesNative = False, gmsh_verbosity = 0, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 522, + "signature": "(self, psi_fn: sympy.Basic, theta: Optional[float] = 0.5, varsymbol: Optional[str] = '\\\\psi', verbose: Optional[bool] = False, bcs = [], order: int = 1, smoothing: float = 0.0)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", - "description": "" - }, - { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", - "description": "" - }, - { - "name": "numSegments", - "type_hint": "int", - "default": "6", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "psi_fn", + "type_hint": "sympy.Basic", + "default": null, "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "theta", + "type_hint": "Optional[float]", + "default": "0.5", "description": "" }, { - "name": "filename", - "type_hint": null, - "default": "None", + "name": "varsymbol", + "type_hint": "Optional[str]", + "default": "'\\\\psi'", "description": "" }, { - "name": "refinement", - "type_hint": null, - "default": "None", + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", "description": "" }, { - "name": "coordinatesNative", + "name": "bcs", "type_hint": null, - "default": "False", + "default": "[]", "description": "" }, { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", + "name": "order", + "type_hint": "int", + "default": "1", "description": "" }, { - "name": "verbose", - "type_hint": null, - "default": "False", + "name": "smoothing", + "type_hint": "float", + "default": "0.0", "description": "" } ], "returns": null, - "existing_docstring": "Create a 3D spherical shell mesh using orange-peel segmentation.\n\nGenerates a spherical shell mesh by dividing the sphere into\nlongitudinal wedge segments, similar to an orange peel. Each segment\nis a 3D wedge extending from the inner to outer radius.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the shell.\nradiusInner : float, default=0.547\n Inner radius of the shell.\ncellSize : float, default=0.1\n Target mesh element size.\nnumSegments : int, default=6\n Number of longitudinal segments.\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ncoordinatesNative : bool, default=False\n If True, use native :math:`(r, \\theta, \\phi)` coordinates\n with periodicity in :math:`\\phi`. If False, use Cartesian\n coordinates with spherical coordinate system overlay.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``LowerPlus``: Inner surface + slice boundaries\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n - ``UpperPlus``: Outer surface + slice boundaries\n - ``Slices``: Radial slice boundaries between segments\n\n The mesh provides boundary normals for free-slip conditions\n on the radial boundaries.\n\nSee Also\n--------\nSegmentedSphericalBall : Solid ball version (no inner boundary).\nSphericalShell : Standard unstructured spherical shell.\nCubedSphere : Alternative structured spherical mesh.\n\nWarnings\n--------\nWhen ``coordinatesNative=True``, this mesh uses PETSc periodicity\nfeatures that may be unreliable. Consider using ``coordinatesNative=False``\nor alternative mesh types for production work.\n\nNotes\n-----\nThe ``LowerPlus`` and ``UpperPlus`` boundaries combine the radial\nsurfaces with the slice boundaries, useful for applying no-slip\nconditions that include the radial walls.\n\nThe coordinate system provides unit vectors via ``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "existing_docstring": null, "harvested_comments": [ - "# Follow the lead of the cubed sphere and make copies of a segment", - "Make boundaries", - "Make surfaces", - "Make volume", - "Make copies" + "a sympy expression for \u03c8; can be scalar or matrix", + "History tracking: deferred initialization and effective order", + "current timestep (set by solver or update_pre_solve)", + "previous timesteps for variable-dt BDF", + "Ensure psi_fn is a sympy Matrix." ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Symbolic", + "is_public": false }, { - "name": "SegmentedSphericalBall", - "kind": "function", - "file": "src/underworld3/meshing/segmented.py", - "line": 683, - "signature": "(radius: float = 1.0, cellSize: float = 0.1, numSegments: int = 6, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, coordinatesNative = False, verbosity = 0, gmsh_verbosity = 0, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 872, + "signature": "(self, mesh: uw.discretisation.Mesh, psi_fn: Union[uw.discretisation.MeshVariable, sympy.Basic], vtype: uw.VarType, degree: int, continuous: bool, V_fn = None, evalf: Optional[bool] = False, theta: Optional[float] = 0.5, varsymbol: Optional[str] = 'u', verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0)", "parameters": [ { - "name": "radius", - "type_hint": "float", - "default": "1.0", + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, "description": "" }, { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", + "name": "psi_fn", + "type_hint": "Union[uw.discretisation.MeshVariable, sympy.Basic]", + "default": null, "description": "" }, { - "name": "numSegments", - "type_hint": "int", - "default": "6", + "name": "vtype", + "type_hint": "uw.VarType", + "default": null, "description": "" }, { "name": "degree", "type_hint": "int", - "default": "1", + "default": null, "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "continuous", + "type_hint": "bool", + "default": null, "description": "" }, { - "name": "filename", + "name": "V_fn", "type_hint": null, "default": "None", "description": "" }, { - "name": "refinement", - "type_hint": null, - "default": "None", + "name": "evalf", + "type_hint": "Optional[bool]", + "default": "False", "description": "" }, { - "name": "coordinatesNative", - "type_hint": null, + "name": "theta", + "type_hint": "Optional[float]", + "default": "0.5", + "description": "" + }, + { + "name": "varsymbol", + "type_hint": "Optional[str]", + "default": "'u'", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", "default": "False", "description": "" }, { - "name": "verbosity", + "name": "bcs", "type_hint": null, - "default": "0", + "default": "[]", "description": "" }, { - "name": "gmsh_verbosity", + "name": "order", "type_hint": null, - "default": "0", + "default": "1", "description": "" }, { - "name": "verbose", + "name": "smoothing", "type_hint": null, - "default": "False", + "default": "0.0", "description": "" } ], "returns": null, - "existing_docstring": "Create a 3D solid spherical ball mesh using orange-peel segmentation.\n\nGenerates a solid sphere mesh (no inner cavity) by dividing it into\nlongitudinal wedge segments extending from the centre to the surface.\n\nParameters\n----------\nradius : float, default=1.0\n Radius of the sphere.\ncellSize : float, default=0.1\n Target mesh element size.\nnumSegments : int, default=6\n Number of longitudinal segments.\ndegree : int, default=1\n Polynomial degree of finite elements.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ncoordinatesNative : bool, default=False\n If True, use native :math:`(r, \\theta, \\phi)` coordinates\n with periodicity in :math:`\\phi`. If False, use Cartesian\n coordinates with spherical coordinate system overlay.\nverbosity : int, default=0\n Deprecated. Use ``verbose`` instead.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Upper``: Outer surface at :math:`r = radius`\n - ``UpperPlus``: Outer surface + slice boundaries\n - ``Centre``: Central point\n - ``Slices``: Radial slice boundaries between segments\n\n The mesh provides boundary normals for free-slip conditions\n on the outer surface.\n\nSee Also\n--------\nSegmentedSphericalShell : Shell version with inner boundary.\nSphericalShell : Standard unstructured spherical shell.\n\nWarnings\n--------\nWhen ``coordinatesNative=True``, this mesh uses PETSc periodicity\nfeatures that may be unreliable. Consider using ``coordinatesNative=False``\nor alternative mesh types for production work.\n\nNotes\n-----\nThis mesh is useful for modelling solid bodies (e.g., planetary cores,\nasteroids) where elements must extend to the centre. The segmented\napproach avoids the pole singularity issues of latitude-longitude grids.\n\nThe coordinate system provides unit vectors via ``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "existing_docstring": null, "harvested_comments": [ - "# Follow the lead of the cubed sphere and make copies of a segment", - "Make boundaries", - "faceLoopi = gmsh.model.geo.addCurveLoop(", - "[edgeWi, edgeEqi, edgeEi], tag=-1, reorient=True", - "Make surfaces" + "sympy function or mesh variable", + "History tracking: deferred initialization and effective order", + "meshVariables are required for:", + "u(t) - evaluation of u_fn at the current time", + "u*(t) - u_* evaluated from" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": false }, { - "name": "SphericalShell", - "kind": "function", - "file": "src/underworld3/meshing/spherical.py", - "line": 30, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1444, + "signature": "(self, mesh: uw.discretisation.Mesh, psi_fn: sympy.Function, V_fn: sympy.Function, vtype: uw.VarType, degree: int, continuous: bool, swarm_degree: Optional[int] = None, swarm_continuous: Optional[bool] = None, varsymbol: Optional[str] = None, verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0, preserve_moments = False, with_forcing_history: bool = False, monotone_mode: Optional[str] = None, theta: float = 0.5, old_frame_traceback: bool = False)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, "description": "" }, { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", + "name": "psi_fn", + "type_hint": "sympy.Function", + "default": null, "description": "" }, { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", + "name": "V_fn", + "type_hint": "sympy.Function", + "default": null, + "description": "" + }, + { + "name": "vtype", + "type_hint": "uw.VarType", + "default": null, "description": "" }, { "name": "degree", "type_hint": "int", - "default": "1", + "default": null, "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "continuous", + "type_hint": "bool", + "default": null, "description": "" }, { - "name": "filename", - "type_hint": null, + "name": "swarm_degree", + "type_hint": "Optional[int]", "default": "None", "description": "" }, { - "name": "refinement", - "type_hint": null, + "name": "swarm_continuous", + "type_hint": "Optional[bool]", "default": "None", "description": "" }, { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", + "name": "varsymbol", + "type_hint": "Optional[str]", + "default": "None", "description": "" }, { "name": "verbose", - "type_hint": null, + "type_hint": "Optional[bool]", "default": "False", "description": "" - } - ], - "returns": null, - "existing_docstring": "Create a 3D spherical shell mesh.\n\nGenerates a tetrahedral mesh on a full spherical shell (or solid\nsphere if ``radiusInner=0``) using Gmsh's OpenCASCADE geometry\nkernel. Provides :math:`(r, \\theta, \\phi)` coordinates for\nconvenient representation of radial problems.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the spherical shell.\nradiusInner : float, default=0.547\n Inner radius of the spherical shell. Set to 0 for a solid\n sphere extending to the centre.\ncellSize : float, default=0.1\n Target mesh element size.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply. Each level\n approximately octruples element count.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n - ``Centre``: Centre point (if radiusInner=0)\n\n The mesh includes a refinement callback that snaps boundary\n nodes back to true spherical geometry after refinement.\n\nSee Also\n--------\nAnnulus : 2D equivalent.\nSphericalShellInternalBoundary : Spherical shell with internal surface.\nCubedSphere : Structured spherical shell mesh.\n\nExamples\n--------\nCreate a spherical shell for mantle convection:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.SphericalShell(\n... radiusOuter=1.0,\n... radiusInner=0.547,\n... cellSize=0.1\n... )\n\nCreate a solid sphere:\n\n>>> mesh = uw.meshing.SphericalShell(\n... radiusOuter=1.0,\n... radiusInner=0.0,\n... cellSize=0.1\n... )\n\nNotes\n-----\nThe inner radius default of 0.547 corresponds approximately to the\nEarth's core-mantle boundary radius ratio (3480/6371).\n\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`\n\nFor free-slip boundary conditions on vector problems (e.g., Stokes),\nuse a penalty on the normal velocity component::\n\n Gamma_N = mesh.Gamma # discrete normal, or\n Gamma_N = mesh.CoordinateSystem.unit_e_0 # analytic radial\n stokes.add_natural_bc(\n penalty * Gamma_N.dot(v_soln.sym) * Gamma_N, \"Upper\"\n )", - "harvested_comments": [ - "discrete normal, or", - "analytic radial", - "Ensure boundaries conform (if refined)", - "This is equivalent to a partial function because it already", - "knows the configuration of THIS spherical mesh and" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "SphericalShellInternalBoundary", - "kind": "function", - "file": "src/underworld3/meshing/spherical.py", - "line": 276, - "signature": "(radiusOuter: float = 1.0, radiusInternal: float = 0.8, radiusInner: float = 0.547, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", - "parameters": [ - { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" }, { - "name": "radiusInternal", - "type_hint": "float", - "default": "0.8", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", + "name": "bcs", + "type_hint": null, + "default": "[]", "description": "" }, { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", + "name": "order", + "type_hint": null, + "default": "1", "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "smoothing", + "type_hint": null, + "default": "0.0", "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "preserve_moments", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "filename", - "type_hint": null, - "default": "None", + "name": "with_forcing_history", + "type_hint": "bool", + "default": "False", "description": "" }, { - "name": "refinement", - "type_hint": null, + "name": "monotone_mode", + "type_hint": "Optional[str]", "default": "None", "description": "" }, { - "name": "gmsh_verbosity", - "type_hint": null, - "default": "0", + "name": "theta", + "type_hint": "float", + "default": "0.5", "description": "" }, { - "name": "verbose", - "type_hint": null, + "name": "old_frame_traceback", + "type_hint": "bool", "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Generates a spherical shell with an internal boundary using Gmsh. The function creates a 3D mesh of a spherical shell\ndefined by outer, internal, and inner radii. Mesh size, polynomial degree, and Gmsh verbosity can be customized.\n\nParameters:\n-----------\nradiusOuter : float, optional\n The outer radius of the spherical shell. Default is 1.0.\n\nradiusInternal : float, optional\n The radius of the internal boundary within the spherical shell. Default is 0.8.\n\nradiusInner : float, optional\n The inner radius of the spherical shell. Default is 0.547.\n\ncellSize : float, optional\n The target size for the mesh elements. This controls the density of the mesh. Default is 0.1.\n\ndegree : int, optional\n The polynomial degree of the finite elements used in the mesh. Default is 1.\n\nqdegree : int, optional\n The quadrature degree for integration. Higher values may improve accuracy but increase computation time. Default is 2.\n\nfilename : str, optional\n The name of the file where the mesh will be saved. If None, a default name is generated based on the radii and mesh size. Default is None.\n\nrefinement : optional\n Refinement level or method for the mesh. Used to increase the resolution of the mesh in certain regions. Default is None.\n\ngmsh_verbosity : int, optional\n Controls the verbosity of Gmsh output. Set to 0 for minimal output, higher numbers for more detailed logs. Default is 0.\n\nverbose : bool, optional\n If True, the function prints additional information during execution. Default is False.\n\nReturns:\n--------\nNone\n The function generates and saves a mesh file according to the specified parameters.\n\nExample:\n--------\nmesh = uw.meshing.SphericalShellInternalBoundary(\n radiusOuter=2.0,\n radiusInternal=1.5,\n radiusInner=1.0,\n cellSize=0.05,\n degree=2,\n qdegree=3,\n filename=\"custom_spherical_shell.msh\",\n gmsh_verbosity=1,\n verbose=True\n)", + "existing_docstring": null, "harvested_comments": [ - "Check if r_i is greater than 0", - "Cut the inner sphere from the outer sphere to create a shell", - "Create another inner sphere with radius r_i (for the internal sphere)", - "Set the maximum characteristic length (mesh size) for the mesh elements", - "Embed a 2D surface into a 3D volume" + "Monotonicity limiter for the SL trace-back result. Bound", + "the FE-interpolated upstream sample to the local data range", + "of psi_star at each trace-back point. Cures the FE Lagrange", + "overshoot pattern in cells with sharp gradients while", + "preserving FE accuracy elsewhere." ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": false }, { - "name": "SegmentofSphere", - "kind": "function", - "file": "src/underworld3/meshing/spherical.py", - "line": 496, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, longitudeExtent: float = 90.0, latitudeExtent: float = 90.0, cellSize: float = 0.1, degree: int = 1, qdegree: int = 2, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False, centroid: Tuple = (0.0, 0.0, 0.0))", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2988, + "signature": "(self, mesh: uw.discretisation.Mesh, psi_fn: sympy.Function, V_fn: sympy.Function, vtype: uw.VarType, degree: int, continuous: bool, varsymbol: Optional[str] = 'u', verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0, fill_param = 3)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", - "description": "" - }, - { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", + "name": "mesh", + "type_hint": "uw.discretisation.Mesh", + "default": null, "description": "" }, { - "name": "longitudeExtent", - "type_hint": "float", - "default": "90.0", + "name": "psi_fn", + "type_hint": "sympy.Function", + "default": null, "description": "" }, { - "name": "latitudeExtent", - "type_hint": "float", - "default": "90.0", + "name": "V_fn", + "type_hint": "sympy.Function", + "default": null, "description": "" }, { - "name": "cellSize", - "type_hint": "float", - "default": "0.1", + "name": "vtype", + "type_hint": "uw.VarType", + "default": null, "description": "" }, { "name": "degree", "type_hint": "int", - "default": "1", + "default": null, "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "continuous", + "type_hint": "bool", + "default": null, "description": "" }, { - "name": "filename", - "type_hint": null, - "default": "None", + "name": "varsymbol", + "type_hint": "Optional[str]", + "default": "'u'", "description": "" }, { - "name": "refinement", + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "bcs", "type_hint": null, - "default": "None", + "default": "[]", "description": "" }, { - "name": "gmsh_verbosity", + "name": "order", "type_hint": null, - "default": "0", + "default": "1", "description": "" }, { - "name": "verbose", + "name": "smoothing", "type_hint": null, - "default": "False", + "default": "0.0", "description": "" }, { - "name": "centroid", - "type_hint": "Tuple", - "default": "(0.0, 0.0, 0.0)", + "name": "fill_param", + "type_hint": null, + "default": "3", "description": "" } ], "returns": null, - "existing_docstring": "Generates a segment of a sphere using Gmsh. This function creates a 3D mesh of a spherical segment defined by outer and inner radii,\nand the extent in longitude and latitude. The mesh can be customized in terms of size, polynomial degree, and verbosity.\n\nParameters:\n-----------\nradiusOuter : float, optional\n The outer radius of the spherical segment. Default is 1.0.\n\nradiusInner : float, optional\n The inner radius of the spherical segment. Default is 0.547.\n\nlongitudeExtent : float, optional\n The angular extent of the segment in the longitudinal direction (in degrees). Default is 90.0.\n\nlatitudeExtent : float, optional\n The angular extent of the segment in the latitudinal direction (in degrees). Default is 90.0.\n\ncellSize : float, optional\n The target size for the mesh elements. This controls the density of the mesh. Default is 0.1.\n\ndegree : int, optional\n The polynomial degree of the finite elements used in the mesh. Default is 1.\n\nqdegree : int, optional\n The quadrature degree for integration. Higher values may improve accuracy but increase computation time. Default is 2.\n\nfilename : str, optional\n The name of the file where the mesh will be saved. If None, a default name is generated based on the parameters. Default is None.\n\nrefinement : optional\n Refinement level or method for the mesh. Used to increase the resolution of the mesh in certain regions. Default is None.\n\ngmsh_verbosity : int, optional\n Controls the verbosity of Gmsh output. Set to 0 for minimal output, higher numbers for more detailed logs. Default is 0.\n\nverbose : bool, optional\n If True, the function prints additional information during execution. Default is False.\n\ncentroid : Tuple[float, float, float], optional\n The coordinates of the centroid (center) of the sphere segment. Default is (0.0, 0.0, 0.0).\n\nReturns:\n--------\nNone\n The function generates and saves a mesh file according to the specified parameters.\n\nExample:\n--------\nmesh = uw.meshing.SegmentofSphere(\n radiusOuter=2.0,\n radiusInner=1.0,\n longitudeExtent=120.0,\n latitudeExtent=60.0,\n cellSize=0.05,\n degree=2,\n qdegree=3,\n filename=\"custom_sphere_segment.msh\",\n centroid=(0.0, 0.0, 0.0),\n gmsh_verbosity=1,\n verbose=True\n)", + "existing_docstring": null, "harvested_comments": [ - "Create segment of sphere", - "Add Physical groups", - "Add the volume entity to a physical group with a high tag number (99999) and name it \"Elements\"", - "Ensure boundaries conform (if refined)", - "This is equivalent to a partial function because it already" + "create a new swarm to manage here", + "History tracking: deferred initialization and effective order", + "current timestep (set by solver or update_pre_solve)", + "previous timesteps for variable-dt BDF", + "BDF/AM coefficient UWexpressions \u2014 routed through PetscDS constants[]" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": false }, { - "name": "CubedSphere", - "kind": "function", - "file": "src/underworld3/meshing/spherical.py", - "line": 773, - "signature": "(radiusOuter: float = 1.0, radiusInner: float = 0.547, numElements: int = 5, degree: int = 1, qdegree: int = 2, simplex: bool = False, filename = None, refinement = None, gmsh_verbosity = 0, verbose = False)", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3375, + "signature": "(self, swarm: uw.swarm.Swarm, psi_fn: sympy.Function, vtype: uw.VarType, degree: int, continuous: bool, varsymbol: Optional[str] = 'u', verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0, step_averaging = 2)", "parameters": [ { - "name": "radiusOuter", - "type_hint": "float", - "default": "1.0", + "name": "swarm", + "type_hint": "uw.swarm.Swarm", + "default": null, "description": "" }, { - "name": "radiusInner", - "type_hint": "float", - "default": "0.547", + "name": "psi_fn", + "type_hint": "sympy.Function", + "default": null, "description": "" }, { - "name": "numElements", - "type_hint": "int", - "default": "5", + "name": "vtype", + "type_hint": "uw.VarType", + "default": null, "description": "" }, { "name": "degree", "type_hint": "int", - "default": "1", + "default": null, "description": "" }, { - "name": "qdegree", - "type_hint": "int", - "default": "2", + "name": "continuous", + "type_hint": "bool", + "default": null, "description": "" }, { - "name": "simplex", - "type_hint": "bool", + "name": "varsymbol", + "type_hint": "Optional[str]", + "default": "'u'", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", "default": "False", "description": "" }, { - "name": "filename", + "name": "bcs", "type_hint": null, - "default": "None", + "default": "[]", "description": "" }, { - "name": "refinement", + "name": "order", "type_hint": null, - "default": "None", + "default": "1", "description": "" }, { - "name": "gmsh_verbosity", + "name": "smoothing", "type_hint": null, - "default": "0", + "default": "0.0", "description": "" }, { - "name": "verbose", + "name": "step_averaging", "type_hint": null, - "default": "False", + "default": "2", "description": "" } ], "returns": null, - "existing_docstring": "Create a cubed-sphere mesh with structured elements.\n\nGenerates a spherical shell mesh using the cubed-sphere projection,\nwhich maps six cube faces onto the sphere. Produces hexahedral\nelements (or tetrahedra if ``simplex=True``) with uniform resolution.\n\nParameters\n----------\nradiusOuter : float, default=1.0\n Outer radius of the spherical shell.\nradiusInner : float, default=0.547\n Inner radius of the spherical shell.\nnumElements : int, default=5\n Number of elements along each edge of each cube face. Total\n elements approximately ``6 * numElements^2`` per radial layer.\ndegree : int, default=1\n Polynomial degree of finite element basis functions.\nqdegree : int, default=2\n Quadrature degree for numerical integration.\nsimplex : bool, default=False\n If False, use hexahedral elements. If True, subdivide into\n tetrahedra (simplex elements).\nfilename : str, optional\n Path to save the mesh file.\nrefinement : int, optional\n Number of uniform refinement levels to apply.\ngmsh_verbosity : int, default=0\n Gmsh output verbosity level.\nverbose : bool, default=False\n Print diagnostic information.\n\nReturns\n-------\nMesh\n A 3D mesh with boundaries:\n\n - ``Lower``: Inner surface at :math:`r = r_{inner}`\n - ``Upper``: Outer surface at :math:`r = r_{outer}`\n\nSee Also\n--------\nSphericalShell : Unstructured tetrahedral spherical mesh.\nSegmentofSphere : Partial spherical shell.\n\nExamples\n--------\nCreate a cubed-sphere mesh with 10 elements per edge:\n\n>>> import underworld3 as uw\n>>> mesh = uw.meshing.CubedSphere(\n... radiusOuter=1.0,\n... radiusInner=0.55,\n... numElements=10\n... )\n\nNotes\n-----\nThe cubed-sphere projection avoids polar singularities present in\nlatitude-longitude grids, providing quasi-uniform element sizes\nacross the sphere. This is particularly advantageous for global\ngeodynamics simulations.\n\nThe mesh coordinate system provides unit vectors via\n``mesh.CoordinateSystem``:\n\n- ``unit_e_0``: radial direction :math:`(r)`\n- ``unit_e_1``: colatitude direction :math:`(\\theta)`\n- ``unit_e_2``: longitude direction :math:`(\\phi)`", + "existing_docstring": null, "harvested_comments": [ - "Make copies", - "if not simplex:", - "Generate Mesh", - "# Note these numbers should not be hard-wired", - "print(f\"Refinement callback - spherical\", flush=True)" + "History tracking: deferred initialization and effective order", + "current timestep (set by solver or update_pre_solve)", + "previous timesteps for variable-dt BDF", + "BDF/AM coefficient UWexpressions \u2014 routed through PetscDS constants[]", + "Initialise to order-1 values" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "units", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 142, - "signature": "(self) -> Optional[str]", - "parameters": [], - "returns": "Optional[str]", - "existing_docstring": "Units for this variable (None if dimensionless).", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceVariable", - "is_public": true + "parent_class": "Lagrangian_Swarm", + "is_public": false }, { - "name": "has_units", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 147, - "signature": "(self) -> bool", - "parameters": [], - "returns": "bool", - "existing_docstring": "Check if this variable has units.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "_apply_unit_aware_scaling", + "kind": "function", + "file": "src/underworld3/systems/solvers.py", + "line": 79, + "signature": "(dt_nondimensional, field, mesh)", + "parameters": [ + { + "name": "dt_nondimensional", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SurfaceVariable", - "is_public": true - }, - { - "name": "data", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 152, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Direct access to vertex data with optional unit awareness.\n\nReturns:\n UnitAwareArray if units are set, otherwise plain numpy array.\n Shape is (n_vertices,) for size=1, or (n_vertices, size) otherwise.\n\nNote:\n When units are set, modifications via array operations automatically\n sync to pyvista storage and mark proxy as stale. For raw numpy arrays,\n call mark_stale() after modifications.", + "existing_docstring": "Helper function to apply unit-aware scaling to timestep estimates.\n\nDetects the units of the velocity field and applies appropriate time scaling\nto convert nondimensional timestep to physical time units.\n\nParameters\n----------\ndt_nondimensional : float or np.ndarray\n The nondimensional timestep estimate\nfield : MeshVariable or SymPy expression (often a Matrix)\n The velocity field - units are detected from this\nmesh : Mesh\n The mesh (may have reference to model with time scales)\n\nReturns\n-------\nfloat or UWQuantity\n Timestep with physical time units if detectable, otherwise nondimensional", "harvested_comments": [ - "Import here to avoid circular imports", - "Create callback to sync changes to pyvista and mark stale" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "Extract a component from field if it's a Matrix (common for velocity)", + "Extract first component: V_fn[0] or V_fn[0,0]", + "Try to get units from the field expression", + "Field has units - verify it has time dimension (as expected for velocity)", + "Get dimensionality: e.g., {'[length]': 1, '[time]': -1} for velocity" ], - "parent_class": "SurfaceVariable", - "is_public": true + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false }, { - "name": "data", + "name": "_create_stress_history_ddt", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 185, - "signature": "(self, values) -> None", + "file": "src/underworld3/systems/solvers.py", + "line": 1217, + "signature": "(self, order = 2)", "parameters": [ { - "name": "values", + "name": "order", "type_hint": null, - "default": null, + "default": "2", "description": "" } ], - "returns": "None", - "existing_docstring": "Set vertex data and mark proxy as stale.\n\nIf values have units (magnitude attribute), the magnitude is extracted\nfor storage in pyvista. Units are tracked separately.", + "returns": null, + "existing_docstring": "Create DFDt for stress history tracking (VE/VEP models).\n\nCalled automatically when a constitutive model with\n``requires_stress_history = True`` is assigned. Can also be called\nexplicitly to pre-create the DFDt with a specific order.\n\nConstitutive models can inject extra SemiLagrangian kwargs via the\n``stress_history_ddt_kwargs`` property \u2014 used e.g. by\n``MaxwellExponentialFlowModel`` to set ``with_forcing_history=True``.", "harvested_comments": [ - "Strip units if present" + "already created", + "Constitutive model may request extra SemiLagrangian kwargs (e.g.", + "with_forcing_history for ETD-2 integration).", + "Stress flux = 2\u00b7viscosity\u00b7E_eff references psi_star[0] in E_eff's", + "history term \u2014 without snapshot substitution the projection of" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "SurfaceVariable", - "is_public": true + "parent_class": "SNES_Stokes", + "is_public": false }, { - "name": "mark_stale", + "name": "_warn_if_monolithic_direct", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 198, - "signature": "(self) -> None", - "parameters": [], - "returns": "None", - "existing_docstring": "Mark the proxy as stale so it will be recomputed on next .sym access.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SurfaceVariable", - "is_public": true - }, - { - "name": "mask", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 203, - "signature": "(self) -> sympy.Expr", - "parameters": [], - "returns": "sympy.Expr", - "existing_docstring": "Distance-based mask: 1 near surface, 0 far away.\n\nUses the surface's signed distance field with the configured profile.\nMust be explicitly applied in expressions: use `var.sym * var.mask`.\n\nReturns:\n sympy.Expr representing the mask (1 near, 0 far)\n\nRaises:\n ValueError: If mask_width was not set in add_variable()\n\nExample:\n >>> friction = surface.add_variable(\"friction\", mask_width=0.1)\n >>> friction.data[:] = 0.6\n >>> eta = friction.sym[0] * friction.mask # Masked value", - "harvested_comments": [ - "Masked value" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SurfaceVariable", - "is_public": true - }, - { - "name": "sym", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 234, - "signature": "(self) -> sympy.Matrix", - "parameters": [], - "returns": "sympy.Matrix", - "existing_docstring": "Symbolic representation for use in expressions.\n\nReturns:\n sympy.Matrix that can be used in Underworld expressions.\n\nNote:\n On first access (or after mark_stale()), this triggers interpolation\n from surface vertices to mesh nodes. The interpolation uses inverse\n distance weighting from nearby surface vertices.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "SurfaceVariable", - "is_public": true - }, - { - "name": "dim", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 413, - "signature": "(self) -> int", - "parameters": [], - "returns": "int", - "existing_docstring": "Spatial dimension (2 or 3).\n\nDetected from mesh.dim if available, otherwise from control points shape.", - "harvested_comments": [ - "Try to get from mesh", - "Infer from control points", - "Default to 3D" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "is_2d", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 438, - "signature": "(self) -> bool", - "parameters": [], - "returns": "bool", - "existing_docstring": "True if this is a 2D surface (1D curve in 2D space).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "symbol", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 443, - "signature": "(self) -> str", + "file": "src/underworld3/systems/solvers.py", + "line": 2252, + "signature": "(self)", "parameters": [], - "returns": "str", - "existing_docstring": "Short LaTeX-friendly symbol for math expressions.\n\nUsed in distance field expressions like $d_F$ instead of\nthe full variable name {surf_fault_distance}.", + "returns": null, + "existing_docstring": "Warn that a monolithic direct solve of the constrained system is a\nserial diagnostic only.\n\nSetting ``pc_type = lu``/``cholesky`` factorises the whole\n:math:`[\\,u, p, h\\,]` saddle point as one block. On the constrained\nsystem this is a KNOWN-BAD path: it does not reproduce the validated\ngrouped-Schur (``selfp``) velocity response (e.g. surface velocity ~4e-3\nvs the ~1e-2 Zhong reference on the 3-D shell) AND it segfaults in\nparallel (the indefinite KKT factorisation is not robust here). The\ngrouped :math:`\\mathbf{u}\\,|\\,[p,h]` field-split is the supported path;\nuse ``pc_type = lu`` only as a serial cross-check, knowing the response is\nnot the benchmark reference.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "SNES_Stokes_Constrained", + "is_public": false }, { - "name": "symbol", + "name": "_maybe_install_auto_gauge", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 452, - "signature": "(self, value: str) -> None", - "parameters": [ - { - "name": "value", - "type_hint": "str", - "default": null, - "description": "" - } - ], - "returns": "None", - "existing_docstring": "Set the math symbol. Marks distance field as stale if changed.", - "harvested_comments": [ - "If distance var exists, it needs to be recreated with new symbol" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "control_points", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 462, - "signature": "(self) -> Optional[np.ndarray]", + "file": "src/underworld3/systems/solvers.py", + "line": 2283, + "signature": "(self)", "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(N, 3) array of control points defining the surface.", + "returns": null, + "existing_docstring": "Pin the (p, h) gauge consistently on an enclosed constrained problem.\n\nConservative \u2014 installs ``set_pressure_gauge`` on the first constraint\nboundary (``reference=0``) only when ALL of the following hold, otherwise\nit is a no-op and the solve path is unchanged:\n\n* ``auto_pressure_gauge`` is left True (the default),\n* we have not already installed it (idempotent across re-solves),\n* at least one multiplier constraint is registered,\n* a pressure null space is active (``petsc_use_nullspace`` / enclosed) \u2014\n this is the gauge-freedom flag; with it off the pressure is determined\n and must not be shifted,\n* no pressure Dirichlet BC already pins the gauge,\n* the user has registered no update callback of their own (e.g. an\n explicit ``set_pressure_gauge`` or a smoother) \u2014 we never clobber it.\n\nPinning the surface-mean pressure makes the raw **pressure** field\npartition-reproducible (measured: the enclosed-shell mean pressure goes\nfrom a ~10% serial-vs-np8 spread to the assembly floor, ~0.4%). It is\nphysics-neutral: the velocity is bit-identical with and without the pin.\nIt does NOT fix the raw **multiplier** ``h`` \u2014 the constant multiplier is\nan independent gauge freedom the pressure pin does not touch \u2014 so dynamic\ntopography must still be read with ``topography(..., reference=\"mean\")``,\nwhich is gauge-invariant by construction.", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "set_control_points", - "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 466, - "signature": "(self, points: np.ndarray) -> None", - "parameters": [ - { - "name": "points", - "type_hint": "np.ndarray", - "default": null, - "description": "" - } - ], - "returns": "None", - "existing_docstring": "Set control points and mark discretization as stale.\n\nArgs:\n points: (N, 2) or (N, 3) array of points. Can include Pint units,\n which will be converted using the model's scaling system.\n For 2D points, z-coordinate is set to 0.", - "harvested_comments": [ - "Handle unit conversion using standard scaling system (ndim)", - "This works with both legacy (get_coefficients) and modern patterns", - "Points have Pint units - convert using scaling system", - "For 2D points, pad with z=0" - ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "vertices", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 502, - "signature": "(self) -> Optional[np.ndarray]", - "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(N, 3) array of vertex positions.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "n_vertices", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 509, - "signature": "(self) -> int", - "parameters": [], - "returns": "int", - "existing_docstring": "Number of vertices in the discretized surface.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "n_triangles", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 516, - "signature": "(self) -> int", - "parameters": [], - "returns": "int", - "existing_docstring": "Number of triangles in the surface.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "normals", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 523, - "signature": "(self) -> Optional[np.ndarray]", - "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(N, 3) array of vertex normals (point normals from pyvista).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "triangles", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 531, - "signature": "(self) -> Optional[np.ndarray]", - "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(M, 3) array of triangle vertex indices.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "face_centers", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 541, - "signature": "(self) -> Optional[np.ndarray]", - "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(M, 3) array of triangle centroids.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Surface", - "is_public": true + "parent_class": "SNES_Stokes_Constrained", + "is_public": false }, { - "name": "face_normals", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 549, - "signature": "(self) -> Optional[np.ndarray]", + "name": "_viscosity_scale", + "kind": "method", + "file": "src/underworld3/systems/solvers.py", + "line": 2346, + "signature": "(self)", "parameters": [], - "returns": "Optional[np.ndarray]", - "existing_docstring": "(M, 3) array of face normals (cell normals from pyvista).", + "returns": null, + "existing_docstring": "A representative scalar viscosity, for sizing the interior screening.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "SNES_Stokes_Constrained", + "is_public": false }, { - "name": "pv_mesh", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 557, + "name": "__init__", + "kind": "method", + "file": "src/underworld3/units.py", + "line": 57, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "PyVista PolyData mesh (None if not discretized).", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "_PintHelper", + "is_public": false }, { - "name": "is_discretized", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 562, - "signature": "(self) -> bool", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 10, + "signature": "(self)", "parameters": [], - "returns": "bool", - "existing_docstring": "Whether the surface has been discretized.", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "Stateful", + "is_public": false }, { - "name": "discretize", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 573, - "signature": "(self, offset: float = 0.01, n_segments: int = None) -> None", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 23, + "signature": "(self, f)", "parameters": [ { - "name": "offset", - "type_hint": "float", - "default": "0.01", - "description": "" - }, - { - "name": "n_segments", - "type_hint": "int", - "default": "None", + "name": "f", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Discretize control points into a surface mesh.\n\nFor 3D surfaces: Uses pyvista delaunay_2d to create a triangulated mesh.\nFor 2D surfaces: Uses scipy to fit a spline and create a polyline.\n\nArgs:\n offset: (3D only) Height offset for delaunay_2d (controls curvature tolerance).\n n_segments: (2D only) Number of line segments. If None, uses number\n of control points minus 1.\n\nRaises:\n ImportError: If pyvista not available\n ValueError: If points too sparse for discretization\n RuntimeError: If discretization fails", - "harvested_comments": [ - "Clear stale flags" - ], - "status": "partial", + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "class_or_instance_method", + "is_public": false }, { - "name": "deform_vertices", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 695, - "signature": "(self, displacement: np.ndarray) -> None", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 81, + "signature": "(self, attr_name = None, matrix_wrap = False, allow_none = True, doc = None)", "parameters": [ { - "name": "displacement", - "type_hint": "np.ndarray", - "default": null, + "name": "attr_name", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "matrix_wrap", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "allow_none", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "doc", + "type_hint": null, + "default": "None", "description": "" } ], - "returns": "None", - "existing_docstring": "Deform surface vertices in-place (no re-discretization).\n\nThis modifies vertex positions while keeping topology fixed.\nNormals are automatically recomputed.\n\nArgs:\n displacement: (n_vertices, 3) array of displacements to add\n\nNote:\n This does NOT update control points. If you want topology changes,\n use set_control_points() instead and call discretize().", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "distance", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 728, - "signature": "(self) -> 'uw.discretisation.MeshVariable'", - "parameters": [], - "returns": "'uw.discretisation.MeshVariable'", - "existing_docstring": "Signed distance from mesh nodes to surface (lazily computed).\n\nThe signed distance is positive on one side of the surface and\nnegative on the other. Use sympy.Abs(surface.distance.sym[0]) for\nunsigned distance, or use influence_function() which does this\nautomatically.\n\nReturns:\n MeshVariable with signed distance values at each mesh node.\n Access .sym[0] for use in expressions.\n\nRaises:\n RuntimeError: If mesh not set or surface not discretized\n\nExample:\n >>> # Use signed distance for different properties on each side\n >>> d = surface.distance.sym[0]\n >>> prop = sympy.Piecewise((upper_value, d > 0), (lower_value, True))\n >>>\n >>> # Use absolute distance for symmetric influence\n >>> mask = sympy.Piecewise((1, sympy.Abs(d) < width), (0, True))", - "harvested_comments": [ - "Use signed distance for different properties on each side", - "Use absolute distance for symmetric influence" - ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "SymbolicProperty", + "is_public": false }, { - "name": "influence_function", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 824, - "signature": "(self, width: float, value_near: Union[float, sympy.Expr] = 1.0, value_far: Union[float, sympy.Expr] = 0.0, profile: str = 'step') -> sympy.Expr", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 198, + "signature": "(self, name, value_fn, description, read_only = False, units = None, validator = None, category = None, attr_name = None)", "parameters": [ { - "name": "width", - "type_hint": "float", + "name": "name", + "type_hint": null, "default": null, "description": "" }, { - "name": "value_near", - "type_hint": "Union[float, sympy.Expr]", - "default": "1.0", + "name": "value_fn", + "type_hint": null, + "default": null, "description": "" }, { - "name": "value_far", - "type_hint": "Union[float, sympy.Expr]", - "default": "0.0", + "name": "description", + "type_hint": null, + "default": null, "description": "" }, { - "name": "profile", - "type_hint": "str", - "default": "'step'", + "name": "read_only", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "validator", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "category", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "attr_name", + "type_hint": null, + "default": "None", "description": "" } ], - "returns": "sympy.Expr", - "existing_docstring": "Create level-set-like influence function based on distance.\n\nCreates a sympy expression that varies from value_near (at the surface)\nto value_far (far from the surface) based on the chosen profile.\n\nUses the absolute value of the signed distance field, so the influence\nis symmetric on both sides of the surface. For asymmetric behavior,\naccess the signed distance directly via surface.distance.sym[0].\n\nArgs:\n width: Characteristic width of the transition zone. Can include\n Pint units (e.g., 500 * u.meter), which will be converted\n using the model's scaling system.\n value_near: Value at/near the surface (can be a scalar or sympy expression)\n value_far: Value far from the surface (can be a scalar or sympy expression)\n profile: Transition profile type:\n - \"step\": Sharp transition at distance = width\n - \"linear\": Linear ramp from 0 to width\n - \"gaussian\": Smooth Gaussian decay\n - \"smoothstep\": C1-continuous Hermite interpolation\n\nReturns:\n sympy.Expr that can be used in Underworld expressions\n\nExample:\n >>> # Step function for fault zone viscosity\n >>> eta = surface.influence_function(\n ... width=0.05,\n ... value_near=0.01,\n ... value_far=1.0,\n ... profile=\"step\",\n ... )\n >>>\n >>> # Gaussian decay for smooth transitions\n >>> eta = surface.influence_function(\n ... width=0.1,\n ... value_near=friction.sym, # Variable on surface\n ... value_far=1.0,\n ... profile=\"gaussian\",\n ... )", + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Step function for fault zone viscosity", - "Gaussian decay for smooth transitions", - "Variable on surface", - "Handle unit conversion for width using standard scaling system (ndim)", - "This works with both legacy (get_coefficients) and modern patterns" + "Expose description as __doc__ for Sphinx autodoc" ], - "status": "complete", - "needs": [], - "parent_class": "Surface", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ExpressionDescriptor", + "is_public": false }, { - "name": "add_variable", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 903, - "signature": "(self, name: str, size: int = 1, proxy_degree: int = 1, units: Optional[str] = None, mask_width: Optional[float] = None, mask_profile: str = 'gaussian') -> SurfaceVariable", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 356, + "signature": "(self, name, value_fn, description, units = None, validator = None, **kwargs)", "parameters": [ { "name": "name", - "type_hint": "str", + "type_hint": null, "default": null, "description": "" }, { - "name": "size", - "type_hint": "int", - "default": "1", + "name": "value_fn", + "type_hint": null, + "default": null, "description": "" }, { - "name": "proxy_degree", - "type_hint": "int", - "default": "1", + "name": "description", + "type_hint": null, + "default": null, "description": "" }, { "name": "units", - "type_hint": "Optional[str]", + "type_hint": null, "default": "None", "description": "" }, { - "name": "mask_width", - "type_hint": "Optional[float]", + "name": "validator", + "type_hint": null, "default": "None", "description": "" }, { - "name": "mask_profile", - "type_hint": "str", - "default": "'gaussian'", + "name": "**kwargs", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "SurfaceVariable", - "existing_docstring": "Add a variable on surface vertices.\n\nCreates a SurfaceVariable stored in pyvista point_data with\nsymbolic access via .sym for use in expressions.\n\nArgs:\n name: Variable name\n size: Number of components (1 for scalar, 3 for vector)\n proxy_degree: Degree of proxy MeshVariable for .sym access\n units: Optional units for this variable (e.g., \"Pa\", \"m/s\")\n mask_width: Width for distance-based mask (enables .mask property)\n mask_profile: Profile for mask function (\"step\", \"linear\", \"gaussian\", \"smoothstep\")\n\nReturns:\n SurfaceVariable that can be modified via .data and used via .sym\n\nExample:\n >>> # Variable with units and mask\n >>> friction = surface.add_variable(\"friction\", size=1, mask_width=0.1)\n >>> friction.data[:] = 0.6\n >>>\n >>> # Use in expressions with explicit masking\n >>> eta = friction.sym[0] * friction.mask", - "harvested_comments": [ - "Variable with units and mask", - "Use in expressions with explicit masking" + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Surface", - "is_public": true + "parent_class": "Parameter", + "is_public": false }, { - "name": "get_variable", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 950, - "signature": "(self, name: str) -> SurfaceVariable", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 402, + "signature": "(self, name, value_fn, description, **kwargs)", "parameters": [ { "name": "name", - "type_hint": "str", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "value_fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "description", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" } ], - "returns": "SurfaceVariable", - "existing_docstring": "Get an existing variable by name.\n\nArgs:\n name: Variable name\n\nReturns:\n SurfaceVariable\n\nRaises:\n KeyError: If variable doesn't exist", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "Surface", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Template", + "is_public": false }, { - "name": "variables", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 965, - "signature": "(self) -> Dict[str, SurfaceVariable]", + "name": "__init__", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 451, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, SurfaceVariable]", - "existing_docstring": "Dictionary of all variables on this surface.", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "uw_object", + "is_public": false }, { - "name": "save", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 971, - "signature": "(self, filename: str) -> None", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 99, + "signature": "(self, message: str, explanation: str = None)", "parameters": [ { - "name": "filename", + "name": "message", "type_hint": "str", "default": null, "description": "" + }, + { + "name": "explanation", + "type_hint": "str", + "default": "None", + "description": "" } ], - "returns": "None", - "existing_docstring": "Save surface with all variables to VTK file.\n\nAll SurfaceVariable data is automatically included in the VTK file\nas point_data arrays.\n\nArgs:\n filename: Output path (.vtk or .vtp)", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "UW_Pause", + "is_public": false }, { - "name": "refinement_metric", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1012, - "signature": "(self, h_near: float, h_far: float, width: float = None, profile: str = 'linear', name: str = None) -> 'MeshVariable'", + "file": "src/underworld3/utilities/_utils.py", + "line": 14, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "rank 0 only builds the data and then broadcasts it", + "get the start time of this piece of code", + "get petsc information", + "get h5py information", + "get mpi4py information" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_uw_record", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/utilities/_utils.py", + "line": 142, + "signature": "(self, seq = '', split = False, *args, **kwargs)", "parameters": [ { - "name": "h_near", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "h_far", - "type_hint": "float", - "default": null, + "name": "seq", + "type_hint": null, + "default": "''", "description": "" }, { - "name": "width", - "type_hint": "float", - "default": "None", + "name": "split", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "profile", - "type_hint": "str", - "default": "'linear'", + "name": "*args", + "type_hint": null, + "default": null, "description": "" }, { - "name": "name", - "type_hint": "str", - "default": "None", + "name": "**kwargs", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Create a metric field for mesh adaptation based on distance from this surface.\n\nReturns a MeshVariable containing refinement metric values that can\nbe passed directly to mesh.adapt(). Higher metric values produce finer\nmesh (smaller elements).\n\nParameters\n----------\nh_near : float\n Target edge length near the surface (smaller = finer mesh).\n Smaller values produce more refinement near the surface.\nh_far : float\n Target edge length far from the surface (larger = coarser mesh).\n Larger values allow coarser mesh far from the surface.\nwidth : float, optional\n Distance over which to transition from h_near to h_far.\n If None, defaults to 2 * h_far.\nprofile : str, optional\n Transition profile: \"linear\", \"smoothstep\", or \"gaussian\".\n Default is \"linear\".\nname : str, optional\n Name for the metric MeshVariable. Defaults to \"{surface_name}_metric\".\n\nReturns\n-------\nMeshVariable\n Scalar MeshVariable containing refinement metric values.\n\nNotes\n-----\n**Metric Tensor Mathematics**\n\nFor isotropic mesh adaptation, MMG/PETSc uses a metric tensor:\n\n.. math::\n\n M = h^{-2} \\cdot I\n\nwhere :math:`h` is the target edge length and :math:`I` is the identity\nmatrix. This relationship is **dimension-independent** - the same formula\napplies in 2D and 3D because the metric defines edge lengths, not areas\nor volumes.\n\nThe adaptation algorithm seeks to make all edges have unit length in the\nmetric space (i.e., :math:`\\mathbf{e}^T M \\mathbf{e} = 1` for edge vector\n:math:`\\mathbf{e}`). Higher metric values produce smaller elements.\n\n**Refinement Ratio**\n\nThe refinement ratio is ``h_far / h_near``. For example, if ``h_near=0.01``\nand ``h_far=0.1``, the mesh will be ~10\u00d7 finer near the surface.\n\n**Element Count Control**\n\nTo maintain approximately the same total element count while refining\nnear the surface, the far-field should use similar h to the original\nmesh's cell size. The refined region is small, so coarsening the far-field\nslightly can compensate.\n\nReferences\n----------\n.. [1] MMG Platform documentation: http://www.mmgtools.org/\n.. [2] Alauzet, F. \"Metric-based anisotropic mesh adaptation\" (2010)\n\nExamples\n--------\n>>> fault = uw.meshing.Surface(\"fault\", mesh, fault_points)\n>>> fault.discretize()\n>>>\n>>> # 10x refinement near fault, maintain original density far away\n>>> metric = fault.refinement_metric(h_near=0.005, h_far=0.05)\n>>> mesh.adapt(metric)", - "harvested_comments": [ - "10x refinement near fault, maintain original density far away", - "Create metric MeshVariable", - "Get distance values directly from the distance MeshVariable", - "Compute target edge lengths based on distance profile", - "Convert to metric tensor: M = 1/h\u00b2 \u00d7 I (isotropic)" + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Surface", - "is_public": true + "parent_class": "CaptureStdout", + "is_public": false }, { - "name": "from_vtk", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1139, - "signature": "(cls, filename: str, mesh: 'Mesh' = None, name: str = None) -> 'Surface'", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 525, + "signature": "(self, level_meshes, builder = 'barycentric', field_id = None)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "level_meshes", + "type_hint": null, "default": null, "description": "" }, { - "name": "mesh", - "type_hint": "'Mesh'", - "default": "None", + "name": "builder", + "type_hint": null, + "default": "'barycentric'", "description": "" }, { - "name": "name", - "type_hint": "str", + "name": "field_id", + "type_hint": null, "default": "None", "description": "" } ], - "returns": "'Surface'", - "existing_docstring": "Load surface from VTK file.\n\nAll point_data arrays in the VTK file are automatically wrapped\nas SurfaceVariables.\n\nArgs:\n filename: Path to VTK file (.vtk or .vtp)\n mesh: Computational mesh (required for .sym access)\n name: Name for the surface. If None, uses filename stem.\n\nReturns:\n Surface with all variables from VTK file\n\nRaises:\n FileNotFoundError: If file doesn't exist\n ImportError: If pyvista not available", - "harvested_comments": [ - "Compute normals if not present", - "Wrap existing point_data as SurfaceVariables", - "Skip built-in normals" - ], - "status": "complete", - "needs": [], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "compute_normals", - "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1193, - "signature": "(self, consistent_normals: bool = True) -> None", - "parameters": [ - { - "name": "consistent_normals", - "type_hint": "bool", - "default": "True", - "description": "" - } - ], - "returns": "None", - "existing_docstring": "Recompute vertex and face normals.\n\nArgs:\n consistent_normals: If True, attempt to make normals consistently oriented", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "Surface", - "is_public": true - }, - { - "name": "flip_normals", - "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1207, - "signature": "(self) -> None", - "parameters": [], - "returns": "None", - "existing_docstring": "Flip the direction of all normals.\n\nThis directly negates the normal vectors. Note that pyvista's\ncompute_normals() uses consistent orientation so we can't rely\non flip_faces() to reverse normals - we must negate them explicitly.", - "harvested_comments": [ - "Ensure normals are computed", - "Directly negate the point normals", - "Also negate cell normals if present" - ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Surface", - "is_public": true + "parent_class": "CustomMGHierarchy", + "is_public": false }, { - "name": "add", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1281, - "signature": "(self, surface: Surface) -> None", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 158, + "signature": "(self, original)", "parameters": [ { - "name": "surface", - "type_hint": "Surface", + "name": "original", + "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Add a surface to the collection.\n\nArgs:\n surface: Surface to add\n\nRaises:\n ValueError: If surface with same name already exists", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", - "is_public": true + "parent_class": "NonDimensionalView", + "is_public": false }, { - "name": "add_from_vtk", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1298, - "signature": "(self, filename: str, mesh: 'Mesh' = None, name: str = None) -> Surface", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 813, + "signature": "(self, sympy_matrix, with_units_func, source_units = None)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "sympy_matrix", + "type_hint": null, "default": null, "description": "" }, { - "name": "mesh", - "type_hint": "'Mesh'", - "default": "None", + "name": "with_units_func", + "type_hint": null, + "default": null, "description": "" }, { - "name": "name", - "type_hint": "str", + "name": "source_units", + "type_hint": null, "default": "None", "description": "" } ], - "returns": "Surface", - "existing_docstring": "Load and add a surface from VTK file.\n\nArgs:\n filename: Path to VTK file\n mesh: Computational mesh\n name: Name for the surface. If None, uses filename stem.\n\nReturns:\n The loaded Surface", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Units of the variable being differentiated", + "Compute and cache units for this derivative matrix" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": false + }, + { + "name": "__init__", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 44, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SurfaceCollection", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "DelayedCallbackManager", + "is_public": false }, { - "name": "remove", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1318, - "signature": "(self, name: str) -> Surface", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 9, + "signature": "(self, filename)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "filename", + "type_hint": null, "default": null, "description": "" } ], - "returns": "Surface", - "existing_docstring": "Remove and return a surface from the collection.\n\nArgs:\n name: Name of surface to remove\n\nReturns:\n The removed Surface\n\nRaises:\n KeyError: If surface not found", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SurfaceCollection", - "is_public": true - }, - { - "name": "names", - "kind": "property", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1347, - "signature": "(self) -> List[str]", - "parameters": [], - "returns": "List[str]", - "existing_docstring": "List of surface names.", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "py2/py3 compatibility, see https://github.com/h5py/h5py/issues/379" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", - "is_public": true + "parent_class": "Xdmf", + "is_public": false }, { - "name": "compute_distance_field", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1351, - "signature": "(self, mesh: 'Mesh', distance_var: 'MeshVariable' = None, variable_name: str = 'surface_distance') -> 'MeshVariable'", + "file": "src/underworld3/constitutive_models.py", + "line": 273, + "signature": "(self, unknowns, material_name: str = None)", "parameters": [ { - "name": "mesh", - "type_hint": "'Mesh'", + "name": "unknowns", + "type_hint": null, "default": null, "description": "" }, { - "name": "distance_var", - "type_hint": "'MeshVariable'", - "default": "None", - "description": "" - }, - { - "name": "variable_name", + "name": "material_name", "type_hint": "str", - "default": "'surface_distance'", + "default": "None", "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Compute minimum distance from mesh points to any surface.\n\nArgs:\n mesh: The mesh to compute distances on\n distance_var: Optional existing MeshVariable to store results\n variable_name: Name for new variable if distance_var is None\n\nReturns:\n MeshVariable with distance values (scalar)", + "returns": null, + "existing_docstring": "Initialize a constitutive model.\n\nParameters\n----------\nunknowns : UnknownSet\n The solver's unknowns (velocity, pressure, etc.)\nmaterial_name : str, optional\n A distinguishing name for this material's symbols.\n If provided, symbols will be subscripted: \u03b7 \u2192 \u03b7_{name}\n Useful when bundling multiple models in MultiMaterialModel.", "harvested_comments": [ - "Ensure all surfaces are discretized", - "Create or use existing variable", - "Get mesh coordinates", - "Initialize with large distance", - "Compute unsigned distance to each surface, take minimum." + "Define / identify the various properties in the class but leave", + "the implementation to child classes. The constitutive tensor is", + "defined as a template here, but should be instantiated via class", + "properties as required.", + "We provide a function that converts gradients / gradient history terms" ], - "status": "complete", - "needs": [], - "parent_class": "SurfaceCollection", - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Constitutive_Model", + "is_public": false }, { - "name": "transfer_normals", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1414, - "signature": "(self, mesh: 'Mesh', coords: np.ndarray = None, normal_var: 'MeshVariable' = None, variable_name: str = 'surface_normals') -> 'MeshVariable'", + "file": "src/underworld3/constitutive_models.py", + "line": 1137, + "signature": "(self, unknowns, order = 1, integrator: str = 'bdf', material_name: str = None)", "parameters": [ { - "name": "mesh", - "type_hint": "'Mesh'", + "name": "unknowns", + "type_hint": null, "default": null, "description": "" }, { - "name": "coords", - "type_hint": "np.ndarray", - "default": "None", + "name": "order", + "type_hint": null, + "default": "1", "description": "" }, { - "name": "normal_var", - "type_hint": "'MeshVariable'", - "default": "None", + "name": "integrator", + "type_hint": "str", + "default": "'bdf'", "description": "" }, { - "name": "variable_name", + "name": "material_name", "type_hint": "str", - "default": "'surface_normals'", + "default": "None", "description": "" } ], - "returns": "'MeshVariable'", - "existing_docstring": "Transfer surface normals to mesh points via nearest-neighbor.\n\nFor each mesh point, finds the closest surface face and copies\nthat face's normal vector.\n\nArgs:\n mesh: The mesh to transfer normals to\n coords: Optional coordinates to query. If None, uses mesh.data\n normal_var: Optional existing MeshVariable\n variable_name: Name for new variable\n\nReturns:\n MeshVariable with normal vectors (3 components)", + "returns": null, + "existing_docstring": "Construct a viscoelastic-plastic flow model.\n\nParameters\n----------\nunknowns : Unknowns\n The solver unknowns (velocity, pressure).\norder : int, default 1\n Time-integration order. Combines with ``integrator``:\n\n - ``integrator='bdf', order=1``: BDF-1 (backward Euler).\n - ``integrator='bdf', order=2``: BDF-2.\n - ``integrator='etd', order=1``: ETD-1 (single-step,\n fully L-stable, recommended default for VEP+yield).\n - ``integrator='etd', order=2``: ETD-2 (single-step\n with linear-quadrature forcing history; accurate on\n smooth VE but **unstable in tight-yield VEP** \u2014\n produces global runaway, see EXPONENTIAL_VE_INTEGRATOR.md\n lessons #7, #9).\nintegrator : str, default \"bdf\"\n Time-integration scheme:\n\n - ``\"bdf\"``: backward differentiation formula on the\n deviatoric-stress rate equation. Production default.\n - ``\"etd\"``: exponential time-differencing \u2014 integrates the\n Maxwell relaxation operator analytically (``\u03b1 = exp(-\u0394t/\u03c4)``).\n ``order=1`` is the recommended default for new code: same\n stability as BDF-1, exact handling of the relaxation factor\n at large ``\u0394t/\u03c4``. ``order=2`` adds linear quadrature on\n the forcing history for higher accuracy on smooth VE\n (4.3\u00d7 more accurate than BDF-2 on ``bench_ve_harmonic``)\n but blows up under active yield in tight-yield TI faults.\n See ``docs/developer/design/EXPONENTIAL_VE_INTEGRATOR.md``.\nmaterial_name : str, optional\n Name identifier for this material.", "harvested_comments": [ - "Ensure all surfaces are discretized", - "Get query coordinates", - "Create or validate output variable", - "Build combined arrays of all face centers and normals", - "Build KDTree and query" + "Store material_name before creating expressions (needed by create_unique_symbol)", + "This may not be defined at initialisation time, set to None until used", + "This may not be defined at initialisation time, set to None until used", + "This may not be well-defined at initialisation time, set to None until used", + "This may not be well-defined at initialisation time, set to None until used" ], - "status": "complete", - "needs": [], - "parent_class": "SurfaceCollection", - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false }, { - "name": "influence_function", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1477, - "signature": "(self, mesh: 'Mesh', width: float, value_near: Union[float, sympy.Expr] = 1.0, value_far: Union[float, sympy.Expr] = 0.0, profile: str = 'step') -> sympy.Expr", + "file": "src/underworld3/constitutive_models.py", + "line": 2676, + "signature": "(self, unknowns, order = 1, integrator: str = 'bdf', fault_weight = None, material_name: str = None)", "parameters": [ { - "name": "mesh", - "type_hint": "'Mesh'", + "name": "unknowns", + "type_hint": null, "default": null, "description": "" }, { - "name": "width", - "type_hint": "float", - "default": null, + "name": "order", + "type_hint": null, + "default": "1", "description": "" }, { - "name": "value_near", - "type_hint": "Union[float, sympy.Expr]", - "default": "1.0", + "name": "integrator", + "type_hint": "str", + "default": "'bdf'", "description": "" }, { - "name": "value_far", - "type_hint": "Union[float, sympy.Expr]", - "default": "0.0", + "name": "fault_weight", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "profile", + "name": "material_name", "type_hint": "str", - "default": "'step'", + "default": "None", "description": "" } ], - "returns": "sympy.Expr", - "existing_docstring": "Create combined influence function from all surfaces.\n\nArgs:\n mesh: Computational mesh\n width: Characteristic width of the transition zone\n value_near: Value at/near surfaces\n value_far: Value far from surfaces\n profile: Transition profile (step, linear, gaussian, smoothstep)\n\nReturns:\n sympy.Expr based on combined distance field", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SurfaceCollection", - "is_public": true + "returns": null, + "existing_docstring": "Construct a transversely isotropic VEP flow model.\n\nParameters\n----------\nunknowns : Unknowns\n Solver unknowns (velocity, pressure).\norder : int, default 1\n Time-integration order. Combines with ``integrator``:\n\n - ``integrator='bdf', order=1``: BDF-1 (backward Euler).\n - ``integrator='bdf', order=2``: BDF-2.\n - ``integrator='etd', order=1``: ETD-1 (single-step,\n fully L-stable, **recommended default for VEP+yield**).\n Reproduces BDF-1 byte-identically on the killer test;\n wins at large ``\u0394t/\u03c4`` where the analytical relaxation\n factor matters.\n - ``integrator='etd', order=2``: ETD-2 (linear-quadrature\n forcing history). 4\u00d7 more accurate than BDF-2 on smooth\n VE but blows up under active yield in tight-yield TI\n faults \u2014 see lessons #7, #9 in EXPONENTIAL_VE_INTEGRATOR.md.\n\n ``integrator='hybrid'`` pins ``order=1``.\nintegrator : str, default \"bdf\"\n Time integration scheme:\n\n - ``'bdf'``: Backward differentiation (default, robust for VEP).\n - ``'etd'``: Exponential time-differencing \u2014 analytical\n relaxation factor ``\u03b1 = exp(-\u0394t/\u03c4)``. Pair with ``order=1``\n for the recommended default; ``order=2`` is available but\n unsafe under active yield (see lessons #7, #9).\n - ``'hybrid'``: **EXPERIMENTAL \u2014 DO NOT USE FOR PRODUCTION.**\n Spatial blend of BDF (inside fault) and ETD (outside\n fault). Phase E investigation: \u03c3 enforcement is\n BDF-class but |u_y| drifts monotonically over cycles\n from shared-history coupling between the two branches.\n See ``docs/developer/design/EXPONENTIAL_VE_INTEGRATOR.md``\n lesson #11. Use ``'bdf'`` for deep-yield fault problems.\n Requires ``fault_weight`` parameter.\nfault_weight : sympy expression, optional\n Spatial weight ``w(x) \u2208 [0, 1]`` selecting BDF (``w=1``) vs\n ETD (``w=0``) per quadrature point. Required when\n ``integrator='hybrid'``. Typically built from the\n ``influence_function`` used to construct ``yield_stress``,\n normalised so that ``w=1`` inside the fault zone (where\n yielding can happen) and ``w=0`` in the bulk (where\n ``\u03c4_y \u2192 \u03c4_y_bulk`` and yielding is structurally\n unreachable). The flux blend is\n ``\u03c3 = w\u00b7\u03c3_BDF + (1-w)\u00b7\u03c3_ETD``.\nmaterial_name : str, optional\n Name identifier for this material.", + "harvested_comments": [ + "7, #9 in EXPONENTIAL_VE_INTEGRATOR.md.", + "11. Use ``'bdf'`` for deep-yield fault problems.", + "Stress history expressions", + "BDF order-blending \u03b1 \u2208 [0, 1]. \u03b1=1 \u2192 pure BDF-2 (default);", + "\u03b1=0 \u2192 pure BDF-1 coefficients; intermediate \u2192 linear blend." + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "create_weakness_function", + "name": "__init__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1519, - "signature": "(self, distance_var: 'MeshVariable', fault_width: float, eta_weak: float = 0.01, eta_background: float = 1.0) -> sympy.Expr", + "file": "src/underworld3/constitutive_models.py", + "line": 3923, + "signature": "(self, unknowns, material_swarmVariable: IndexSwarmVariable, constitutive_models: list, normalize_levelsets: bool = False)", "parameters": [ { - "name": "distance_var", - "type_hint": "'MeshVariable'", + "name": "unknowns", + "type_hint": null, "default": null, "description": "" }, { - "name": "fault_width", - "type_hint": "float", + "name": "material_swarmVariable", + "type_hint": "IndexSwarmVariable", "default": null, "description": "" }, { - "name": "eta_weak", - "type_hint": "float", - "default": "0.01", + "name": "constitutive_models", + "type_hint": "list", + "default": null, "description": "" }, { - "name": "eta_background", - "type_hint": "float", - "default": "1.0", + "name": "normalize_levelsets", + "type_hint": "bool", + "default": "False", "description": "" } ], - "returns": "sympy.Expr", - "existing_docstring": "Create Piecewise viscosity function for fault weakness.\n\nDEPRECATED: Use influence_function() instead.\n\nArgs:\n distance_var: MeshVariable containing distances\n fault_width: Width of the weak zone\n eta_weak: Viscosity within weak zone\n eta_background: Viscosity outside weak zone\n\nReturns:\n sympy.Piecewise expression", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SurfaceCollection", - "is_public": true - }, - { - "name": "mesh", - "kind": "property", - "file": "src/underworld3/model.py", - "line": 149, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "The primary mesh for this model", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "list_meshes", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 174, - "signature": "(self) -> Dict[int, Any]", - "parameters": [], - "returns": "Dict[int, Any]", - "existing_docstring": "List all meshes registered with this model", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_mesh", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 178, - "signature": "(self, mesh_id: int)", - "parameters": [ - { - "name": "mesh_id", - "type_hint": "int", - "default": null, - "description": "" - } + "existing_docstring": "Parameters\n----------\nunknowns : UnknownSet\n The solver's authoritative unknowns (:math:`\\mathbf{u}`,\n :math:`D\\mathbf{F}/Dt`, :math:`D\\mathbf{u}/Dt`).\nmaterial_swarmVariable : IndexSwarmVariable\n Index variable tracking material distribution on particles.\nconstitutive_models : list of Constitutive_Model\n Pre-configured constitutive models for each material.\nnormalize_levelsets : bool, optional\n Whether to normalize level-set functions to enforce partition of unity.\n Set to True if IndexSwarmVariable does not maintain partition of unity.\n Default: False (assumes IndexSwarmVariable maintains partition of unity)", + "harvested_comments": [ + "Validate compatibility before initialization", + "Ensure model count matches material indices", + "CRITICAL: Share solver's unknowns with all constituent models", + "Composite model doesn't have its own material_name - constituents do" ], - "returns": null, - "existing_docstring": "Get a mesh by ID from the model registry", - "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "MultiMaterialConstitutiveModel", + "is_public": false }, { - "name": "set_primary_mesh", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 182, - "signature": "(self, mesh)", + "file": "src/underworld3/coordinates.py", + "line": 502, + "signature": "(self, coordinate_system)", "parameters": [ { - "name": "mesh", + "name": "coordinate_system", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set a mesh as the primary mesh for this model.\n\nParameters:\n-----------\nmesh : uw.discretisation.Mesh\n Mesh to set as primary (must already be registered)", + "existing_docstring": "Initialize geographic coordinate accessor.\n\nParameters\n----------\ncoordinate_system : CoordinateSystem\n The coordinate system object with GEOGRAPHIC type", "harvested_comments": [ - "Register the mesh first", - "Just update the primary mesh pointer" + "Cache for coordinate arrays", + "Set by _compute_coordinates" ], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "GeographicCoordinateAccessor", + "is_public": false }, { - "name": "set_mesh", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 325, - "signature": "(self, mesh)", + "file": "src/underworld3/coordinates.py", + "line": 956, + "signature": "(self, coordinate_system)", "parameters": [ { - "name": "mesh", + "name": "coordinate_system", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Set or replace the primary mesh for this model.\n\nThis method handles:\n- Registering mesh if not already registered\n- Setting as primary mesh\n- Incrementing version for change tracking\n\nParameters:\n-----------\nmesh : uw.discretisation.Mesh\n The new mesh to use as primary for this model", - "harvested_comments": [], + "existing_docstring": "Initialize spherical/polar coordinate accessor.\n\nParameters\n----------\ncoordinate_system : CoordinateSystem\n The coordinate system object with SPHERICAL or CYLINDRICAL2D type", + "harvested_comments": [ + "2 for polar (cdim=2 mesh), 3 for spherical (cdim=3 mesh \u2014", + "either a 3-D volume mesh or a 2-manifold in 3-space).", + "Always branch on cdim, not dim: spherical coords are a", + "function of the embedded Cartesian (x, y, z) which has", + "cdim components." + ], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "SphericalCoordinateAccessor", + "is_public": false }, { - "name": "get_variable", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 389, - "signature": "(self, name: str, mesh = None, swarm = None)", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 158, + "signature": "(self, varname = None, mesh = None, num_components = None, vtype = None, degree = 1, continuous = True, varsymbol = None, _register = True, units = None, units_backend = None, remesh_policy = None)", "parameters": [ { - "name": "name", - "type_hint": "str", - "default": null, + "name": "varname", + "type_hint": null, + "default": "None", "description": "" }, { @@ -22207,216 +47659,214 @@ "description": "" }, { - "name": "swarm", + "name": "num_components", "type_hint": null, "default": "None", "description": "" - } - ], - "returns": null, - "existing_docstring": "Get a variable by name from the model registry.\n\nParameters:\n-----------\nname : str\n Variable name to look up\nmesh : Mesh, optional\n If provided, look for variable specifically on this mesh\nswarm : Swarm, optional\n If provided, look for variable specifically on this swarm\n\nReturns:\n--------\nVariable or None", - "harvested_comments": [ - "Try exact name first", - "If mesh/swarm specified, verify it matches", - "Look for qualified name", - "Look for qualified name", - "No specific container requested, return whatever we found" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_qualified_name", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 443, - "signature": "(self, variable)", - "parameters": [ + }, { - "name": "variable", + "name": "vtype", "type_hint": null, - "default": null, + "default": "None", "description": "" - } - ], - "returns": null, - "existing_docstring": "Get the fully qualified name for a variable.\n\nReturns the name that uniquely identifies this variable in the model registry,\nwhich may include mesh/swarm ID qualifiers to resolve namespace conflicts.\n\nParameters:\n-----------\nvariable : MeshVariable or SwarmVariable\n Variable to get qualified name for\n\nReturns:\n--------\nstr or None : Qualified name if found, None otherwise", - "harvested_comments": [ - "Search through all registered names" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "transfer_variable_data", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 465, - "signature": "(self, source_var, target_var)", - "parameters": [ + }, { - "name": "source_var", + "name": "degree", "type_hint": null, - "default": null, + "default": "1", "description": "" }, { - "name": "target_var", + "name": "continuous", "type_hint": null, - "default": null, + "default": "True", + "description": "" + }, + { + "name": "varsymbol", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "_register", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "units_backend", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "remesh_policy", + "type_hint": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Transfer data from source variable to target variable using global_evaluate.\n\nThis enables persistent variable identity across mesh changes by transferring\ndata from variables on old meshes to variables on new meshes.\n\nParameters:\n-----------\nsource_var : MeshVariable or SwarmVariable\n Source variable to transfer data from\ntarget_var : MeshVariable or SwarmVariable\n Target variable to transfer data to\n\nReturns:\n--------\nbool : True if transfer successful, False otherwise", + "existing_docstring": "Initialize MeshVariable (only called for NEW objects).\n\nRetrieves initialization parameters from __new__ and handles DM reconstruction.", "harvested_comments": [ - "Import global_evaluate for mesh-to-mesh transfer", - "Get target coordinates based on variable type", - "Use attribute detection for wrapper compatibility", - "Mesh variable (direct or wrapped)", - "Swarm variable" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "list_variables", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 514, - "signature": "(self) -> Dict[str, Any]", - "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "List all variables registered with this model", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "Only initialize if this is a new object (not returned existing)", + "Already initialized", + "Get parameters - either from __new__ (via _init_params) or direct arguments", + "Parameters from __new__ method", + "Direct initialization (should not happen with __new__ pattern, but for safety)" ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "list_swarms", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 518, - "signature": "(self) -> Dict[str, Any]", - "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "List all swarms registered with this model", - "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "_BaseMeshVariable", + "is_public": false }, { - "name": "add_solver", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 522, - "signature": "(self, name: str, solver)", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 90, + "signature": "(self, varname: Union[str, list], mesh: 'Mesh', num_components: Union[int, tuple] = None, vtype: Optional['uw.VarType'] = None, degree: int = 1, continuous: bool = True, varsymbol: Union[str, list] = None, persistent: bool = False, units: Optional[str] = None, units_backend: Optional[str] = None, **kwargs)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "varname", + "type_hint": "Union[str, list]", "default": null, "description": "" }, { - "name": "solver", - "type_hint": null, + "name": "mesh", + "type_hint": "'Mesh'", "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Register a solver with this model", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_solver", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 527, - "signature": "(self, name: str)", - "parameters": [ + }, + { + "name": "num_components", + "type_hint": "Union[int, tuple]", + "default": "None", + "description": "" + }, + { + "name": "vtype", + "type_hint": "Optional['uw.VarType']", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "continuous", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "varsymbol", + "type_hint": "Union[str, list]", + "default": "None", + "description": "" + }, + { + "name": "persistent", + "type_hint": "bool", + "default": "False", + "description": "" + }, { - "name": "name", - "type_hint": "str", + "name": "units", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "units_backend", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Get a solver by name from the model registry", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Initialize enhanced mesh variable.\n\nArgs:\n varname: Variable name\n mesh: Mesh object\n num_components: Number of components (1=scalar, 2/3=vector, etc.)\n vtype: Variable type (optional)\n degree: Polynomial degree\n continuous: Whether variable is continuous\n varsymbol: Symbol for LaTeX representation\n persistent: Whether to enable persistence features\n units: Units for this variable (optional)\n units_backend: Units backend to use ('pint' or 'sympy')\n **kwargs: Additional arguments passed to base MeshVariable", + "harvested_comments": [ + "Check if already initialized (to prevent duplicate initialization)", + "Store configuration FIRST (before any method calls)", + "Weak reference to avoid circular deps", + "Create base variable without registration (we handle registration ourselves)", + "Never let base variable register itself" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "EnhancedMeshVariable", + "is_public": false }, { - "name": "define_parameter", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 531, - "signature": "(self, name: str, ptype = None, **kwargs)", + "file": "src/underworld3/function/quantities.py", + "line": 53, + "signature": "(self, value: Union[float, int, np.ndarray], units: Optional[str] = None, _normalise_offset: bool = True)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "value", + "type_hint": "Union[float, int, np.ndarray]", "default": null, "description": "" }, { - "name": "ptype", - "type_hint": null, + "name": "units", + "type_hint": "Optional[str]", "default": "None", "description": "" }, { - "name": "**kwargs", - "type_hint": null, - "default": null, + "name": "_normalise_offset", + "type_hint": "bool", + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": "Define a new parameter with validation rules.\n\nNOTE: Parameter system not yet implemented. Use model.materials dict directly.\n\nParameters:\n-----------\nname : str\n Parameter path (e.g., 'material.viscosity', 'solver.tolerance')\nptype : ParameterType, optional\n Parameter type for validation (not used yet)\n**kwargs : dict\n Additional arguments", + "existing_docstring": "Initialize a UWQuantity.\n\nParameters\n----------\nvalue : float, int, or array-like\n The dimensional value\nunits : str or Pint Unit, optional\n Units specification\n_normalise_offset : bool, default True\n Internal. When True (the user-input boundary), an offset\n temperature unit (``degC`` / ``degF``) is converted to absolute\n kelvin on construction so that the stored quantity is always\n multiplicative \u2014 no internal code (arithmetic, non-dimensional-\n isation) ever meets a non-multiplicative unit. Set False only on\n the output boundary (:meth:`to`) so a ``.to(\"degC\")`` display\n result is allowed to keep its offset unit.", "harvested_comments": [ - "TODO: Implement when parameter system is ready" + "Store value as numpy array or scalar", + "Scalar - store directly", + "Handle units", + "Accept both strings and Pint Unit objects", + "Already a Pint Unit" ], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "set_material", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 551, - "signature": "(self, name: str, properties: Dict[str, Any])", + "file": "src/underworld3/meshing/faults.py", + "line": 96, + "signature": "(self, name: str, points: np.ndarray = None)", "parameters": [ { "name": "name", @@ -22425,1562 +47875,1427 @@ "description": "" }, { - "name": "properties", - "type_hint": "Dict[str, Any]", - "default": null, + "name": "points", + "type_hint": "np.ndarray", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Set material properties (simple dictionary).\n\nParameters:\n-----------\nname : str\n Material name (e.g., 'mantle', 'crust', 'plume')\nproperties : dict\n Dictionary of property_name -> value/expression\n\nExample:\n--------\n>>> model.set_material('mantle', {'viscosity': 1e21, 'density': 3300})", + "existing_docstring": "Create a fault surface.\n\nArgs:\n name: Identifier for this fault segment.\n points: (N, 3) array of 3D points, or None for an empty fault.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "FaultSurface", + "is_public": false }, { - "name": "get_material", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 569, - "signature": "(self, name: str)", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "file": "src/underworld3/meshing/faults.py", + "line": 401, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Get material properties by name", + "existing_docstring": "Create an empty fault collection.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "FaultCollection", + "is_public": false }, { - "name": "set_reference_quantities", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 573, - "signature": "(self, verbose = False, nondimensional_scaling = True, **quantities)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 394, + "signature": "(self, name: str, surface: 'Surface', size: int = 1, proxy_degree: int = 1, existing: bool = False, units: Optional[str] = None, mask_width: Optional[float] = None, mask_profile: str = 'gaussian')", "parameters": [ { - "name": "verbose", - "type_hint": null, + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "surface", + "type_hint": "'Surface'", + "default": null, + "description": "" + }, + { + "name": "size", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "proxy_degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "existing", + "type_hint": "bool", "default": "False", "description": "" }, { - "name": "nondimensional_scaling", - "type_hint": null, - "default": "True", + "name": "units", + "type_hint": "Optional[str]", + "default": "None", "description": "" }, { - "name": "**quantities", - "type_hint": null, - "default": null, + "name": "mask_width", + "type_hint": "Optional[float]", + "default": "None", + "description": "" + }, + { + "name": "mask_profile", + "type_hint": "str", + "default": "'gaussian'", "description": "" } ], "returns": null, - "existing_docstring": "Set reference quantities for automatic units scaling.\n\nThis method enables the hybrid SymPy+Pint units workflow by allowing users\nto specify their problem in natural units, from which the system derives\noptimal scaling for numerical conditioning.\n\nBy default, this automatically enables non-dimensionalization for solvers,\nensuring consistent behavior between user-facing units and solver internals.\n\nParameters:\n-----------\nverbose : bool, optional\n If True, print diagnostic information about dimensional analysis\n and scale derivation. Default: False.\nnondimensional_scaling : bool, optional\n Enable automatic non-dimensionalization for solvers (default: True).\n When True (recommended), solver operations work in non-dimensional\n [0-1] space while user-facing values remain in physical units.\n Set to False for expert mode (dimensional units only, no scaling).\n **Warning**: Disabling this may cause numerical conditioning issues\n and inconsistencies in unit conversions.\n**quantities : dict\n Named reference quantities using Pint units or UWQuantity objects, e.g.:\n - mantle_viscosity=1e21*uw.units.Pa*uw.units.s\n - plate_velocity=5*uw.units.cm/uw.units.year\n - domain_depth=3000*uw.units.km\n OR using uw.quantity():\n - domain_depth=uw.quantity(2900, \"km\")\n\nExample:\n--------\n>>> # Standard usage (recommended for most users)\n>>> model.set_reference_quantities(\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... domain_depth=3000*uw.units.km\n... )\n# \u2713 Units system active with automatic non-dimensionalization\n\n>>> # Also accepts UWQuantity objects\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(2900, \"km\"),\n... mantle_viscosity=uw.quantity(1e21, \"Pa*s\")\n... )\n\n>>> # Expert mode (not recommended - dimensional units without scaling)\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(2900, \"km\"),\n... nondimensional_scaling=False\n... )\n# \u26a0 Expert mode: Units active WITHOUT non-dimensionalization\n\n>>> # With diagnostic output\n>>> model.set_reference_quantities(\n... verbose=True,\n... domain_depth=uw.quantity(500, \"m\"),\n... mantle_temperature=uw.quantity(1300, \"K\")\n... )\n\nNotes:\n------\nThis method creates a Pint-native registry with model-specific constants\nusing the _constants pattern for optimal numerical conditioning.\n\nThe default behavior (nondimensional_scaling=True) ensures:\n- User-facing values in physical units (km, Myr, Pa*s, etc.)\n- Solver operations in well-conditioned non-dimensional [0-1] space\n- Automatic conversions handled transparently\n\nRaises:\n-------\nRuntimeError\n If called after a mesh has been created (units are locked)", + "existing_docstring": "Create a variable on surface vertices.\n\nArgs:\n name: Variable name (key in pyvista point_data)\n surface: Parent Surface object\n size: Number of components per vertex (1 for scalar, 3 for vector)\n proxy_degree: Degree of proxy MeshVariable for .sym access\n existing: If True, wraps existing point_data (for loading from VTK)\n units: Optional units for this variable (e.g., \"Pa\", \"m/s\")\n mask_width: Width for distance-based mask (enables .mask property)\n mask_profile: Profile for mask function (\"step\", \"linear\", \"gaussian\", \"smoothstep\")", "harvested_comments": [ - "Standard usage (recommended for most users)", - "\u2713 Units system active with automatic non-dimensionalization", - "Also accepts UWQuantity objects", - "Expert mode (not recommended - dimensional units without scaling)", - "\u26a0 Expert mode: Units active WITHOUT non-dimensionalization" + "Create array in pyvista's point_data (unless wrapping existing)", + "Proxy MeshVariable for .sym access (created lazily)" ], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "SurfaceVariable", + "is_public": false }, { - "name": "get_reference_quantities", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 758, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Get the reference quantities for this model.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/meshing/surfaces.py", + "line": 649, + "signature": "(self, name: str, mesh: 'Mesh' = None, control_points: np.ndarray = None, symbol: str = None)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": "None", + "description": "" + }, + { + "name": "control_points", + "type_hint": "np.ndarray", + "default": "None", + "description": "" + }, + { + "name": "symbol", + "type_hint": "str", + "default": "None", + "description": "" + } ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "fundamental_scales", - "kind": "property", - "file": "src/underworld3/model.py", - "line": 763, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Access the derived fundamental scales for dimensional analysis.\n\nReturns the canonical fundamental scaling quantities [L], [M], [T], [\u03b8]\nthat were automatically derived from the reference quantities via dimensional analysis.\nThese scales are used throughout the system for automatic non-dimensionalization.\n\nThe system extracts these fundamental scales from user-provided reference quantities\n(which can have any domain-specific names) through pure dimensional analysis.\nThis provides a domain-agnostic API independent of parameter naming.\n\nReturns\n-------\ndict\n Dictionary mapping canonical dimension names to their scaling quantities:\n - 'length': Length scale [L] (e.g., 1000 m)\n - 'time': Time scale [T] (e.g., 1 Myr)\n - 'mass': Mass scale [M] (e.g., 1e24 kg)\n - 'temperature': Temperature scale [\u03b8] (e.g., 1000 K)\n - Plus any additional dimensions (velocity, viscosity, pressure, etc.)\n\nExample\n-------\n>>> model = uw.get_default_model()\n>>> model.set_reference_quantities(\n... domain_depth=uw.quantity(2900, \"km\"),\n... plate_velocity=uw.quantity(5, \"cm/year\"),\n... mantle_viscosity=uw.quantity(1e21, \"Pa*s\"),\n... temperature_difference=uw.quantity(3000, \"K\")\n... )\n>>> # Access derived fundamental scales (NOT the user-provided reference quantities)\n>>> L0 = model.fundamental_scales['length'] # 2900 km\n>>> T0 = model.fundamental_scales['time'] # ~1.8 Myr\n>>> eta0 = model.fundamental_scales['viscosity'] # 1e21 Pa*s\n>>> DeltaT = model.fundamental_scales['temperature'] # 3000 K\n\nNotes\n-----\n- These are the ROUNDED fundamental scales stored internally after dimensional analysis\n- The scales may have been rounded to nice values for O(1) numerical conditioning\n- Use these scales for physical interpretation of non-dimensional solutions\n- Do NOT use user-provided reference quantity names (domain-specific)", + "existing_docstring": "Create a surface.\n\nParameters\n----------\nname : str\n Identifier for this surface.\nmesh : Mesh, optional\n Computational mesh (required for ``.sym`` access and distance field).\ncontrol_points : ndarray, optional\n (N, 3) array of 3D points defining the surface.\n If None, the surface is empty and must be loaded or\n have points set later.\nsymbol : str, optional\n Short LaTeX-friendly symbol for math display (e.g., ``\"F\"``).\n If None, defaults to first letter of name capitalized.", "harvested_comments": [ - "Access derived fundamental scales (NOT the user-provided reference quantities)" + "Register with mesh for adaptation notifications", + "Math symbol for clean LaTeX display", + "Default: first letter capitalized (e.g., \"main_fault\" -> \"M\")", + "Default to the full (unique) name. The old `name[0].upper()`", + "collapsed distinct surfaces sharing a first letter (e.g." ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "Surface", + "is_public": false }, { - "name": "has_units", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 809, + "file": "src/underworld3/meshing/surfaces.py", + "line": 1975, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Check if this model has units scaling enabled.", + "existing_docstring": "Create an empty surface collection.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "SurfaceCollection", + "is_public": false }, { - "name": "validate_reference_quantities", + "name": "__init__", "kind": "method", "file": "src/underworld3/model.py", - "line": 813, - "signature": "(self, raise_on_error = False)", + "line": 144, + "signature": "(self, name: Optional[str] = None, **kwargs)", "parameters": [ { - "name": "raise_on_error", + "name": "name", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Validate that all registered variables with units have required reference quantities.\n\nThis method checks each variable and reports any missing reference quantities\nneeded to properly derive scaling coefficients.\n\nParameters:\n-----------\nraise_on_error : bool, optional\n If True, raise ValueError on validation failure. If False (default),\n return validation results without raising.\n\nReturns:\n--------\ndict\n Validation results with keys:\n - 'valid': bool - Overall validation status\n - 'warnings': list of warning messages\n - 'errors': list of error messages\n\nExample:\n--------\n>>> model = uw.get_default_model()\n>>> model.set_reference_quantities(domain_depth=uw.quantity(1000, \"km\"))\n>>> mesh = uw.meshing.StructuredQuadBox(...)\n>>> v = uw.discretisation.MeshVariable('v', mesh, 2, degree=2, units='m/s')\n>>> result = model.validate_reference_quantities()\n>>> if not result['valid']:\n... print(\"\\n\".join(result['errors']))", - "harvested_comments": [ - "Check all registered variables", - "Skip variables without units", - "Validate this variable", - "Also check if scaling_coefficient is still 1.0" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_coordinate_unit", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 873, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Get the coordinate unit for this model.\n\nReturns the length unit from reference quantities if set,\notherwise returns None (dimensionless).\n\nReturns:\n--------\npint.Unit or None\n Coordinate unit object (e.g., ) or None if no units set\n\nExample:\n--------\n>>> model.set_reference_quantities(domain_depth=uw.quantity(500, \"km\"))\n>>> model.get_coordinate_unit()\n # Or if using engineering rounding\n\nChanged in 2025-10-16: Now returns pint.Unit objects instead of strings\nfor consistency and to enable natural unit arithmetic (e.g., var.units / mesh.units).", + "existing_docstring": "Initialize a new Model instance.\n\nParameters\n----------\nname : str, optional\n Human-readable name for this model instance\n**kwargs : dict\n Additional arguments for Pydantic BaseModel", "harvested_comments": [ - "Or if using engineering rounding", - "Check if we have reference quantities with length scale", - "Look for any length-dimensioned quantity", - "Check if this is a Pint quantity (has _pint_qty attribute)", - "Get the Pint unit object directly (not the string property)" + "Handle name generation before calling super().__init__", + "Model-dwelling tracker, auto-registered so snapshot/restore", + "manage time/step/dt and any user-added quantities for free.", + "Set initial state if not provided", + "Transition through initializing to configured" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": "Model", - "is_public": true + "is_public": false }, { - "name": "set_as_default", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 949, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/_params.py", + "line": 146, + "signature": "(self, **defaults)", + "parameters": [ + { + "name": "**defaults", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Explicitly set this model as the default model.\n\nThis is useful when working with multiple models (e.g., loading from disk)\nand you want to explicitly control which model is active.\n\nReturns:\n--------\nModel\n Returns self for method chaining\n\nExample:\n--------\n>>> model1 = uw.Model()\n>>> model2 = uw.Model()\n>>> model2.set_as_default() # Make model2 the active default\n>>> assert uw.get_default_model() is model2", + "existing_docstring": "Initialize parameters with defaults, checking for CLI overrides.\n\nArgs:\n **defaults: Parameter names and default values.\n Use uw_ prefix for collision avoidance.", "harvested_comments": [ - "Make model2 the active default" + "Use object.__setattr__ to avoid triggering our custom __setattr__", + "Ensure command-line options (-uw_*) are loaded into the PETSc options", + "database BEFORE we read overrides. petsc4py populates this DB from", + "sys.argv automatically on some platforms but NOT others (e.g. Gadi),", + "where Params previously fell back silently to the defaults even though" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "Params", + "is_public": false }, { - "name": "get_unit_aliases", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 1306, - "signature": "(self) -> dict", - "parameters": [], - "returns": "dict", - "existing_docstring": "Get all available unit aliases for user reference.\n\nReturns\n-------\ndict\n Dictionary mapping dimensions to their available aliases.\n\nExamples\n--------\n>>> model.get_unit_aliases()\n{\n 'length': {\n 'ascii': ['L_ref', 'L_scale', 'L_model', 'length_scale'],\n 'unicode': '\u2112',\n 'display': '\u2112',\n 'technical': '_1000km'\n },\n ...\n}", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 24, + "signature": "(self, *args, **kwargs)", + "parameters": [ + { + "name": "*args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "**kwargs", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "derive_fundamental_scalings", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1680, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Derive fundamental scaling units (length, time, mass, temperature) from reference quantities.\n\nThis method analyzes the dimensional structure of reference quantities to automatically\ndetermine optimal fundamental scalings for the problem domain.\n\nReturns\n-------\ndict\n Dictionary of fundamental scalings with keys like '[length]', '[time]', '[mass]', '[temperature]'\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> scalings = model.derive_fundamental_scalings()\n>>> scalings['[length]'] # Derived from plate_velocity\n>>> scalings['[time]'] # Derived from plate_velocity\n>>> scalings['[temperature]'] # Direct from mantle_temperature", - "harvested_comments": [ - "Derived from plate_velocity", - "Derived from plate_velocity", - "Direct from mantle_temperature", - "Check for over-determined systems before proceeding", - "Import units (Pint) for dimensional analysis" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_fundamental_scales", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1931, - "signature": "(self) -> dict", - "parameters": [], - "returns": "dict", - "existing_docstring": "Get fundamental scales in user-friendly format.\n\nReturns the derived fundamental scaling quantities (length, time, mass, temperature)\nthat the model uses for dimensional analysis. These are the base scales from which\nall other quantities are converted to model units.\n\nIMPORTANT: Returns the ROUNDED scales stored in _fundamental_scales, not re-derived.\nThe scales may have been rounded to nice values for O(1) numerical conditioning.\n\nReturns\n-------\ndict\n Dictionary mapping dimension names to their scaling quantities:\n - 'length': Length scale quantity (e.g., 1000 m, rounded from 500 m)\n - 'time': Time scale quantity (e.g., 1 Myr, rounded)\n - 'mass': Mass scale quantity (e.g., 1e11 kg, rounded)\n - 'temperature': Temperature scale quantity (e.g., 1000 K, rounded)\n - Plus any additional dimensions detected (current, substance, etc.)\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... domain_depth=500*uw.units.m, # Will round to 1000 m\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> scales = model.get_fundamental_scales()\n>>> scales['length'] # 1000 meter (rounded, not 500)", + "existing_docstring": "Initialize dimensionality tracking attributes", "harvested_comments": [ - "Will round to 1000 m", - "1000 meter (rounded, not 500)", - "Return the ROUNDED scales, not re-derived ones!", - "The _fundamental_scales dict contains rounded values for O(1) conditioning", - "_fundamental_scales already has user-friendly keys ('length', 'time', etc.)" + "Default: no scaling" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "DimensionalityMixin", + "is_public": false }, { - "name": "get_scale_for_dimensionality", + "name": "__init__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 1978, - "signature": "(self, dimensionality: dict)", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 47, + "signature": "(self, name, index, system, pretty_str = None, latex_str = None, units = None)", "parameters": [ { - "name": "dimensionality", - "type_hint": "dict", + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "index", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "system", + "type_hint": null, "default": null, "description": "" + }, + { + "name": "pretty_str", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "latex_str", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Get appropriate reference scale for given dimensionality.\n\nFirst checks if user explicitly provided a reference quantity with this exact\ndimensionality (e.g., diffusivity=1e-6 m\u00b2/s). If found, uses that directly.\nOtherwise, computes from fundamental scales (length, time, mass, temperature).\n\nThis design ensures that explicit user-provided scales take precedence over\nderived scales, which is important for physical accuracy. For example, thermal\ndiffusivity (~1e-6 m\u00b2/s) is orders of magnitude different from L\u00b2/t derived\nfrom mantle convection length and time scales.\n\nParameters\n----------\ndimensionality : dict\n Pint dimensionality dictionary, e.g., {'[length]': 1, '[time]': -1}\n for velocity, {'[mass]': 1, '[length]': -1, '[time]': -2} for pressure\n\nReturns\n-------\npint.Quantity\n Reference scale with appropriate dimensionality\n\nRaises\n------\nValueError\n If dimensionality requires scales not available in fundamental scales\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_depth=uw.quantity(2900, \"km\"), ...)\n>>> velocity_dim = {'[length]': 1, '[time]': -1}\n>>> v_scale = model.get_scale_for_dimensionality(velocity_dim)\n>>> # v_scale = 2900 km / (time_scale) in m/s\n\n>>> pressure_dim = {'[mass]': 1, '[length]': -1, '[time]': -2}\n>>> p_scale = model.get_scale_for_dimensionality(pressure_dim)\n>>> # p_scale = M/(L\u00b7T\u00b2) in Pa\n\n>>> # Explicit diffusivity takes precedence over L\u00b2/t\n>>> model.set_reference_quantities(\n... length=uw.quantity(2900, \"km\"),\n... time=uw.quantity(1e15, \"s\"),\n... diffusivity=uw.quantity(1e-6, \"m**2/s\") # Explicit!\n... )\n>>> diff_dim = {'[length]': 2, '[time]': -1}\n>>> diff_scale = model.get_scale_for_dimensionality(diff_dim)\n>>> # diff_scale = 1e-6 m\u00b2/s (explicit), NOT L\u00b2/t = 8.41e-3 m\u00b2/s", - "harvested_comments": [ - "v_scale = 2900 km / (time_scale) in m/s", - "p_scale = M/(L\u00b7T\u00b2) in Pa", - "Explicit diffusivity takes precedence over L\u00b2/t", - "diff_scale = 1e-6 m\u00b2/s (explicit), NOT L\u00b2/t = 8.41e-3 m\u00b2/s", - "PRIORITY 1: Check if user explicitly provided a reference quantity" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_model_base_units", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2094, - "signature": "(self) -> dict", - "parameters": [], - "returns": "dict", - "existing_docstring": "Get model base units in compact, parseable SI prefix form.\n\nReturns a dictionary mapping dimension names to their compact SI unit representations.\nUses Pint's to_compact() to automatically select readable SI prefixes (e.g., 'Mm' for\nmegameters, 'Ps' for petaseconds).\n\nReturns\n-------\ndict\n Dictionary with dimension names as keys and compact SI unit strings as values\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> base_units = model.get_model_base_units()\n>>> base_units['length'] # \"2.9 megameter\" (parseable)\n>>> base_units['time'] # \"1.83 petasecond\" (parseable)", + "existing_docstring": "Initialize the unit-aware coordinate with units information.", "harvested_comments": [ - "\"2.9 megameter\" (parseable)", - "\"1.83 petasecond\" (parseable)", - "Convert to compact SI prefix form (e.g., Mm, Ps, etc.)", - "Return as parseable string like \"2.9 megameter\"", - "Fallback to base units if compact fails" + "BaseScalar.__init__ also doesn't take 'name'" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "UnitAwareBaseScalar", + "is_public": false }, { - "name": "get_scale_summary", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2212, - "signature": "(self) -> str", - "parameters": [], - "returns": "str", - "existing_docstring": "Get a human-readable summary of all fundamental scales.\n\nReturns a formatted string showing the fundamental scales derived from\nreference quantities, including how each scale was derived and what\nreference quantities it makes close to unity.\n\nReturns\n-------\nstr\n Multi-line string with formatted scale summary\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> print(model.get_scale_summary())\nFundamental Scales Summary:\n\nLength Scale: 2900 kilometer\n - From: mantle_depth\n - Makes: mantle_depth \u2248 1.0 in model units\n\nTime Scale: 580 kilometer\u00b7year/centimeter\n - From: length_scale \u00f7 plate_velocity\n - Makes: plate_velocity \u2248 1.0 in model units\n\nMass Scale: 1.68e+27 kg (equivalent)\n - From: mantle_viscosity \u00d7 length_scale \u00d7 time_scale\n - Makes: mantle_viscosity \u2248 1.0 in model units\n\nTemperature Scale: 1500 kelvin\n - From: mantle_temperature\n - Makes: mantle_temperature \u2248 1.0 in model units", + "name": "_dm_stack_bcs", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 451, + "signature": "(dm, boundaries, stacked_bc_label_name)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundaries", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "stacked_bc_label_name", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Define display order and nice names", - "Use Pint's smart formatting capabilities", - "Show derivation source with user's terminology", - "Show what this makes ~1.0 in model units", - "Add any additional scales not in the standard order" + "Load this up on the stack" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "list_derived_scales", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2380, - "signature": "(self) -> dict", - "parameters": [], - "returns": "dict", - "existing_docstring": "List which scales were derived vs. directly specified.\n\nReturns a dictionary categorizing fundamental scales by how they were obtained:\neither directly from reference quantities or derived from compound quantities.\n\nReturns\n-------\ndict\n Dictionary with keys:\n - 'direct': List of (dimension, source) tuples for directly specified scales\n - 'derived': List of (dimension, derivation) tuples for derived scales\n - 'missing': List of standard dimensions that couldn't be determined\n\nExamples\n--------\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> derivation = model.list_derived_scales()\n>>> derivation['direct'] # [('length', 'mantle_depth'), ('temperature', 'mantle_temperature')]\n>>> derivation['derived'] # [('time', 'length_scale \u00f7 plate_velocity'), ('mass', 'mantle_viscosity \u00d7 length_scale \u00d7 time_scale')]\n>>> derivation['missing'] # []", - "harvested_comments": [ - "[('length', 'mantle_depth'), ('temperature', 'mantle_temperature')]", - "[('time', 'length_scale \u00f7 plate_velocity'), ('mass', 'mantle_viscosity \u00d7 length_scale \u00d7 time_scale')]", - "Standard dimensions we expect to see", - "Categorize each scale found", - "Check if it's a direct mapping (starts with \"from \" and no operators)" + "name": "_is_h5_attr_scalar", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 748, + "signature": "(value: Any) -> bool", + "parameters": [ + { + "name": "value", + "type_hint": "Any", + "default": null, + "description": "" + } ], - "status": "partial", + "returns": "bool", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "validate_dimensional_completeness", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2456, - "signature": "(self, required_dimensions = None) -> dict", + "name": "_mesh_coords_key", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 142, + "signature": "(mesh_name: str) -> str", "parameters": [ { - "name": "required_dimensions", - "type_hint": null, - "default": "None", + "name": "mesh_name", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": "dict", - "existing_docstring": "Validate if reference quantities provide complete dimensional coverage.\n\nChecks whether the current reference quantities span enough dimensions\nto derive fundamental scales for all required dimensions, and provides\nsuggestions for completing under-determined systems.\n\nParameters\n----------\nrequired_dimensions : list, optional\n List of dimensions required (e.g., ['length', 'time', 'mass', 'temperature']).\n If None, uses the standard 4-dimension set.\n\nReturns\n-------\ndict\n Validation result containing:\n - 'status': 'complete', 'under_determined', or 'no_reference_quantities'\n - 'missing_dimensions': List of missing dimensions (if any)\n - 'suggestions': List of suggested reference quantities to add\n - 'derivable_dimensions': List of dimensions that can be derived\n - 'analysis': Human-readable analysis string\n\nExamples\n--------\n>>> # Under-determined system\n>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)\n>>> result = model.validate_dimensional_completeness()\n>>> result['status'] # 'under_determined'\n>>> result['missing_dimensions'] # ['time', 'mass', 'temperature']\n\n>>> # Complete system\n>>> model.set_reference_quantities(\n... mantle_depth=2900*uw.units.km,\n... plate_velocity=5*uw.units.cm/uw.units.year,\n... mantle_viscosity=1e21*uw.units.Pa*uw.units.s,\n... mantle_temperature=1500*uw.units.K\n... )\n>>> result = model.validate_dimensional_completeness()\n>>> result['status'] # 'complete'", - "harvested_comments": [ - "Under-determined system", - "'under_determined'", - "['time', 'mass', 'temperature']", - "Complete system", - "Get what dimensions we can actually derive" + "returns": "str", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "set_scaling_mode", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2674, - "signature": "(self, mode = 'exact')", + "name": "_meshvar_key", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 146, + "signature": "(mesh_name: str, var_clean_name: str) -> str", "parameters": [ { - "name": "mode", - "type_hint": null, - "default": "'exact'", + "name": "mesh_name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "var_clean_name", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Set the scaling mode for fundamental scales.\n\nParameters\n----------\nmode : {'exact', 'readable'}\n Scaling mode to use:\n - 'exact': Reference quantities scale to exactly 1.0 (default)\n - 'readable': Reference quantities scale to O(1) with nice round numbers\n\nExamples\n--------\n>>> # Default exact mode: reference quantities become exactly 1.0\n>>> model.set_scaling_mode('exact')\n>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)\n>>> model.to_model_units(2900*uw.units.km) # \u2192 UWQuantity(1.0, 'model_length_units')\n\n>>> # Readable mode: reference quantities become O(1) with nice scales\n>>> model.set_scaling_mode('readable')\n>>> model.set_reference_quantities(mantle_depth=2900*uw.units.km)\n>>> model.to_model_units(2900*uw.units.km) # \u2192 UWQuantity(2.9, 'model_length_units')\n>>> # Internal scale becomes 1000 km instead of 2900 km", - "harvested_comments": [ - "Default exact mode: reference quantities become exactly 1.0", - "\u2192 UWQuantity(1.0, 'model_length_units')", - "Readable mode: reference quantities become O(1) with nice scales", - "\u2192 UWQuantity(2.9, 'model_length_units')", - "Internal scale becomes 1000 km instead of 2900 km" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "Model", - "is_public": true - }, - { - "name": "get_scaling_mode", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2707, - "signature": "(self) -> str", - "parameters": [], "returns": "str", - "existing_docstring": "Get the current scaling mode.\n\nReturns\n-------\nstr\n Current scaling mode: 'exact' or 'readable'", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "show_optimal_units", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2919, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Display the optimal units implied by this model's reference quantities.\n\nShows fundamental scalings (length, time, mass, temperature) derived from\nreference quantities and suggests optimal units for various physical quantities.", - "harvested_comments": [ - "# Optimal Units for Model: {self.name}\"]", - "Show fundamental scalings", - "## Fundamental Scalings\")", - "Derive optimal units for common quantities", - "## Recommended Units for This Problem\")" + "name": "_swarm_coords_key", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 150, + "signature": "(swarm_name: str) -> str", + "parameters": [ + { + "name": "swarm_name", + "type_hint": "str", + "default": null, + "description": "" + } ], - "status": "partial", + "returns": "str", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "scale_to_physical", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3250, - "signature": "(self, coordinates, dimension = 'length')", + "name": "_swarmvar_key", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 154, + "signature": "(swarm_name: str, var_clean_name: str) -> str", "parameters": [ { - "name": "coordinates", - "type_hint": null, + "name": "swarm_name", + "type_hint": "str", "default": null, "description": "" }, { - "name": "dimension", - "type_hint": null, - "default": "'length'", + "name": "var_clean_name", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Convert dimensionless model coordinates to physical units.\n\nThis method converts model-unit coordinate arrays (where the model domain\nis scaled to ~1.0) back to physical units using the model's fundamental scales.\n\nParameters\n----------\ncoordinates : array-like\n Dimensionless coordinate array in model units\ndimension : str, default 'length'\n Fundamental dimension to use for scaling ('length', 'time', 'mass', 'temperature')\n\nReturns\n-------\nUWQuantity\n Coordinates in physical units with appropriate units\n\nExamples\n--------\n>>> model.set_reference_quantities(domain_length=1000*uw.units.km, ...)\n>>> mesh = uw.meshing.StructuredQuadBox(minCoords=(-3,-3), maxCoords=(3,3), ...)\n>>> # mesh.points are in model units (dimensionless, domain spans -3 to 3)\n>>> physical_coords = model.scale_to_physical(mesh.points)\n>>> # Result: coordinates in kilometers, spanning -3000 to 3000 km\n\nRaises\n------\nValueError\n If no reference quantities set or dimension not available", - "harvested_comments": [ - "mesh.points are in model units (dimensionless, domain spans -3 to 3)", - "Result: coordinates in kilometers, spanning -3000 to 3000 km", - "Get fundamental scales", - "Get the scale for this dimension", - "Import here to avoid circular imports" + "returns": "str", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "to_model_units", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3311, - "signature": "(self, quantity)", + "name": "_capture_mesh", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 200, + "signature": "(snap: Snapshot, mesh) -> None", "parameters": [ { - "name": "quantity", + "name": "snap", + "type_hint": "Snapshot", + "default": null, + "description": "" + }, + { + "name": "mesh", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Safely coerce any quantity to model units using smart protocol pattern.\n\nThis method is designed to be safe to call repeatedly and handles edge cases:\n1. Does nothing if model has no units (no reference quantities set)\n2. Does nothing if the quantity is already in model units\n3. Does nothing if the quantity is dimensionless\n4. Uses protocol pattern for extensible conversion\n\nParameters\n----------\nquantity : Any\n Quantity to convert (UWQuantity, Pint quantity, numeric, etc.)\n\nReturns\n-------\nUWQuantity or original quantity\n Converted quantity in model units, or original if no conversion needed", - "harvested_comments": [ - "1) Do nothing if there are no units (no reference quantities set)", - "2) Do nothing if quantity is already in model units", - "Check if it's a UWQuantity with model_units=True flag", - "3) Early check for dimensionless quantities - do nothing", - "Quick dimensionality check without full conversion" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "has_units_active", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3563, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Check if the units system is active (reference quantities have been set).\n\nReturns\n-------\nbool\n True if reference quantities have been set, False otherwise.", + "returns": "None", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "to_model_magnitude", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3574, - "signature": "(self, quantity, expected_dimension = None)", + "name": "_capture_swarm", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 246, + "signature": "(snap: Snapshot, swarm) -> None", "parameters": [ { - "name": "quantity", - "type_hint": null, + "name": "snap", + "type_hint": "Snapshot", "default": null, "description": "" }, { - "name": "expected_dimension", + "name": "swarm", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Convert quantity to model units and extract numerical magnitude.\n\nThis is a convenience method that combines to_model_units() with magnitude\nextraction for internal use. It handles the common pattern of converting\nuser inputs (which may have units) to raw numerical values in model units\nfor internal computations.\n\nParameters\n----------\nquantity : Any\n Quantity to convert (UWQuantity, Pint quantity, numeric, tuple, etc.)\nexpected_dimension : str, optional\n Expected dimension like '[length]', '[time]', etc. If provided and\n the quantity is a plain number when units are active, a warning is\n issued to alert the user that they may have forgotten units.\n Common values: '[length]', '[time]', '[temperature]', '[mass]'\n\nReturns\n-------\nfloat, int, or array-like\n Raw numerical magnitude in model units (dimensionless)\n\nExamples\n--------\n>>> model.set_reference_quantities(length_scale=1000*uw.units.km)\n>>> coords_physical = [100*uw.units.km, 200*uw.units.km]\n>>> coords_model = model.to_model_magnitude(coords_physical)\n>>> coords_model\n[0.1, 0.2] # In model units (dimensionless)\n\n>>> # Also works with plain numbers (pass-through)\n>>> model.to_model_magnitude([0.1, 0.2])\n[0.1, 0.2]\n\n>>> # With expected_dimension, warns on plain numbers when units active\n>>> model.to_model_magnitude(6370, expected_dimension='[length]')\nUserWarning: Plain number 6370 passed for [length] parameter when units\nare active. If you intended physical units, use uw.quantity(6370, 'km').\nValue is being interpreted as 6370 model units.\n\n>>> # Works with time\n>>> dt_physical = 1000 * uw.units.year\n>>> dt_model = model.to_model_magnitude(dt_physical)\n\nNotes\n-----\nThis method is intended for internal use at API boundaries where user\ninputs need to be converted to model units for computations. The conversion\nrespects dimensional analysis and reference quantities.\n\nWhen `expected_dimension` is provided, this acts as a \"gatekeeper\" to catch\npotential bugs where users forget to attach units to their values.", - "harvested_comments": [ - "In model units (dimensionless)", - "Also works with plain numbers (pass-through)", - "With expected_dimension, warns on plain numbers when units active", - "Works with time", - "Handle tuples/lists recursively (for coordinate inputs)" + "returns": "None", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "from_model_magnitude", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3693, - "signature": "(self, magnitude, dimensions)", + "name": "_build_mesh_payload", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 356, + "signature": "(snap: Snapshot, mesh_name: str) -> dict", "parameters": [ { - "name": "magnitude", - "type_hint": null, + "name": "snap", + "type_hint": "Snapshot", "default": null, "description": "" }, { - "name": "dimensions", - "type_hint": null, + "name": "mesh_name", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Convert numerical magnitude in model units back to physical quantity.\n\nThis is the inverse of to_model_magnitude() - it takes a raw numerical value\nin model units and wraps it with appropriate physical units based on the\ndimensional specification.\n\nParameters\n----------\nmagnitude : float, int, or array-like\n Numerical value(s) in model units (dimensionless)\ndimensions : str or Pint dimensionality\n Dimensional specification like '[length]', '[time]', '[length]/[time]', etc.\n Can also be a Pint dimensionality object from quantity.dimensionality\n\nReturns\n-------\nUWQuantity\n Physical quantity with appropriate units based on model reference scales\n\nExamples\n--------\n>>> model.set_reference_quantities(length_scale=1000*uw.units.km)\n>>> coords_model = np.array([0.1, 0.2]) # Model coordinates\n>>> coords_physical = model.from_model_magnitude(coords_model, '[length]')\n>>> coords_physical\n[100, 200] km # Back to physical units\n\n>>> # Works with composite dimensions\n>>> velocity_model = 1.0 # Model velocity magnitude\n>>> velocity_physical = model.from_model_magnitude(velocity_model, '[length]/[time]')\n\n>>> # If no reference quantities, returns SI base units\n>>> model_no_refs = uw.Model()\n>>> result = model_no_refs.from_model_magnitude(100, '[length]')\n>>> result\n100 m # SI base units when no scaling defined\n\nNotes\n-----\nThis method is intended for converting internal model-unit values back to\nphysical quantities when returning results to users. It ensures consistent\nunit handling across API boundaries.", - "harvested_comments": [ - "Model coordinates", - "Back to physical units", - "Works with composite dimensions", - "Model velocity magnitude", - "If no reference quantities, returns SI base units" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true - }, - { - "name": "to_dict", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3866, - "signature": "(self) -> Dict[str, Any]", - "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "Export model configuration to dictionary for serialization.\n\nProvides enhanced serialization with SymPy expression conversion\nand PETSc state capture for complete reproducibility.\n\nReturns\n-------\ndict\n Model configuration dictionary suitable for JSON/YAML export", + "returns": "dict", + "existing_docstring": null, "harvested_comments": [ - "Convert materials with SymPy expression handling" + "Topology is None in v1; v1.2 mesh-rebuild path will populate", + "this slot (e.g., section view data) without bumping the", + "schema version, because v1 reads ignore the key." ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "export_configuration", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3910, - "signature": "(self) -> Dict[str, Any]", - "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "Alias for to_dict() for backward compatibility", + "name": "_build_swarm_payload", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 374, + "signature": "(snap: Snapshot, swarm_name: str) -> dict", + "parameters": [ + { + "name": "snap", + "type_hint": "Snapshot", + "default": null, + "description": "" + }, + { + "name": "swarm_name", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "dict", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "from_dict", + "name": "__setattr__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 3914, - "signature": "(self, config: Dict[str, Any])", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 70, + "signature": "(self, name, value)", "parameters": [ { - "name": "config", - "type_hint": "Dict[str, Any]", + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Import model configuration from dictionary.\n\nHandles enhanced serialization format with SymPy expression reconstruction.\nNote: Only imports materials and metadata for now.\nMesh/variables/swarms must be recreated.\n\nParameters\n----------\nconfig : dict\n Configuration dictionary from to_dict() or YAML export", + "existing_docstring": null, "harvested_comments": [ - "Import materials with SymPy expression reconstruction", - "Reconstruct SymPy expression from string", - "Fall back to string" + "Respect class-level data descriptors \u2014 notably the `state`", + "property. Without this guard, `tracker.state = ...` (done by", + "the snapshot machinery on restore) would be captured as a", + "managed quantity instead of invoking the property setter,", + "and restore would silently no-op. `state` is therefore a" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "ModelTracker", + "is_public": false }, { - "name": "to_yaml", + "name": "__getattr__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 3955, - "signature": "(self, file_path: Optional[str] = None) -> str", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 86, + "signature": "(self, name)", "parameters": [ { - "name": "file_path", - "type_hint": "Optional[str]", - "default": "None", + "name": "name", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "str", - "existing_docstring": "Export model configuration to YAML format.\n\nParameters\n----------\nfile_path : str, optional\n If provided, write YAML to this file path\n\nReturns\n-------\nstr\n YAML string representation of model configuration", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "__getattr__ only fires when normal lookup fails, so it never", + "shadows real attributes or class properties (state,", + "instance_number, ...)." + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "ModelTracker", + "is_public": false }, { - "name": "from_yaml", + "name": "__delattr__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 3978, - "signature": "(self, yaml_content: str = None, file_path: str = None)", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 100, + "signature": "(self, name)", "parameters": [ { - "name": "yaml_content", - "type_hint": "str", - "default": "None", - "description": "" - }, - { - "name": "file_path", - "type_hint": "str", - "default": "None", + "name": "name", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Import model configuration from YAML.\n\nParameters\n----------\nyaml_content : str, optional\n YAML string to parse\nfile_path : str, optional\n Path to YAML file to load", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "ModelTracker", + "is_public": false }, { - "name": "from_yaml_file", + "name": "__contains__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4001, - "signature": "(cls, file_path: str) -> 'Model'", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 110, + "signature": "(self, name)", "parameters": [ { - "name": "file_path", - "type_hint": "str", + "name": "name", + "type_hint": null, "default": null, "description": "" } ], - "returns": "'Model'", - "existing_docstring": "Create a new Model instance from YAML file.\n\nParameters\n----------\nfile_path : str\n Path to YAML configuration file\n\nReturns\n-------\nModel\n New model instance with loaded configuration", - "harvested_comments": [ - "Extract model-specific fields" + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true + "parent_class": "ModelTracker", + "is_public": false }, { - "name": "capture_petsc_state", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4028, - "signature": "(self) -> Dict[str, str]", + "file": "src/underworld3/checkpoint/tracker.py", + "line": 117, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, str]", - "existing_docstring": "Capture current PETSc options database state.\n\nReturns all PETSc options currently set, enabling complete\nreproducibility of simulation parameters.\n\nReturns\n-------\ndict\n Dictionary of PETSc option names and values", - "harvested_comments": [ - "Get all currently set PETSc options", - "PETSc doesn't provide a direct way to list all options,", - "but we can capture commonly used ones and any that were set", - "This is a simplified implementation - full implementation would", - "require more sophisticated PETSc option introspection" + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": true + "parent_class": "ModelTracker", + "is_public": false }, { - "name": "restore_petsc_state", + "name": "__cinit__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4087, - "signature": "(self, petsc_options: Optional[Dict[str, str]] = None)", - "parameters": [ - { - "name": "petsc_options", - "type_hint": "Optional[Dict[str, str]]", - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/ckdtree.pyx", + "line": 86, + "signature": "def __cinit__( self,\n points_input not None: numpy.ndarray ) :", + "parameters": [], "returns": null, - "existing_docstring": "Restore PETSc options database from captured state.\n\nParameters\n----------\npetsc_options : dict, optional\n PETSc options to restore. If None, uses self.petsc_state", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "set_petsc_option", + "name": "__dealloc__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4116, - "signature": "(self, option: str, value: str)", - "parameters": [ - { - "name": "option", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "value", - "type_hint": "str", - "default": null, - "description": "" - } - ], + "file": "src/underworld3/ckdtree.pyx", + "line": 112, + "signature": "def __dealloc__(self):", + "parameters": [], "returns": null, - "existing_docstring": "Set a PETSc option and track it in model state.\n\nParameters\n----------\noption : str\n PETSc option name (without leading -)\nvalue : str\n Option value", - "harvested_comments": [ - "Track in model state" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "view", + "name": "__setattr__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4143, - "signature": "(self, verbose: int = 0, show_materials: bool = True, show_petsc: bool = False)", - "parameters": [ - { - "name": "verbose", - "type_hint": "int", - "default": "0", - "description": "" - }, + "file": "src/underworld3/constitutive_models.py", + "line": 109, + "signature": "(self, name, value)", + "parameters": [ { - "name": "show_materials", - "type_hint": "bool", - "default": "True", + "name": "name", + "type_hint": null, + "default": null, "description": "" }, { - "name": "show_petsc", - "type_hint": "bool", - "default": "False", + "name": "value", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Display a concise summary of the model contents.\n\nParameters\n----------\nverbose : int, default 0\n Verbosity level:\n 0 = Basic summary\n 1 = Include variable details and material properties\n 2 = Include solver information and metadata\nshow_materials : bool, default True\n Whether to show materials summary\nshow_petsc : bool, default False\n Whether to show PETSc options (can be lengthy)\n\nExample\n-------\n>>> model.view() # Basic summary\n>>> model.view(verbose=1) # Detailed view\n>>> model.view(verbose=2, show_petsc=True) # Full details", + "existing_docstring": null, "harvested_comments": [ - "Basic summary", - "Detailed view", - "Full details", - "Build markdown content", - "Model: {self.name}\")" + "Private/internal attributes are always allowed", + "Walk the MRO looking for a matching descriptor or property", + "Valid descriptor \u2014 let it handle the set", + "Not a known descriptor \u2014 likely a name mismatch bug" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "_ParameterBase", + "is_public": false }, { - "name": "view", + "name": "_object_viewer", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4339, + "file": "src/underworld3/constitutive_models.py", + "line": 671, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Display comprehensive model information following the established view() pattern.\n\nShows model configuration, units setup, registered components, and provides\nguidance for setting up units if not configured.", - "harvested_comments": [ - "Build markdown content", - "# Model: {self.name}\"]", - "Model state and basic info", - "Mesh information", - "## Primary Mesh\")" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Model", - "is_public": true + "parent_class": "Constitutive_Model", + "is_public": false }, { - "name": "get_default_model", - "kind": "function", - "file": "src/underworld3/model.py", - "line": 4491, - "signature": "()", + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 890, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Get or create the default model for this UW3 session.\n\nThe default model automatically registers all meshes, swarms, variables,\nand solvers created during the session, enabling serialization and\nmodel orchestration without explicit user interaction.\n\nReturns\n-------\nModel\n The default model instance for this session\n\nExample\n-------\n>>> import underworld3 as uw\n>>> model = uw.get_default_model()\n>>> print(model) # See all registered objects\n>>> config = model.to_dict() # Serialize model", + "existing_docstring": null, "harvested_comments": [ - "See all registered objects", - "Serialize model" + "# feedback on this instance" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "ViscousFlowModel", + "is_public": false }, { - "name": "reset_default_model", - "kind": "function", - "file": "src/underworld3/model.py", - "line": 4517, - "signature": "()", + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1095, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Reset the default model to a fresh instance.\n\nUseful for testing or starting a new simulation in an interactive session.\nAll previously registered objects will be orphaned from the model registry.\n\nAlso resets global state including strict units mode to default (ON).\n\nReturns\n-------\nModel\n New default model instance\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.reset_default_model() # Start fresh", + "existing_docstring": null, "harvested_comments": [ - "Start fresh", - "Reset strict units mode to default (ON)" + "# feedback on this instance" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "ViscoPlasticFlowModel", + "is_public": false }, { - "name": "to_petsc_options", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 4586, - "signature": "(self) -> Dict[str, str]", + "name": "_plastic_effective_viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1723, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, str]", - "existing_docstring": "Convert configuration to PETSc options dictionary.\n\nReturns\n-------\ndict\n PETSc options suitable for setting via Model.set_petsc_option()", + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Stokes solver options", - "Advection-diffusion options" + "Use the effective strain rate (including elastic history) for the", + "yield criterion. This must use the same order-dependent BDF", + "coefficients as the stress formula.", + ".rewrite(sympy.Piecewise)" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ThermalConvectionConfig", - "is_public": true + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false }, { - "name": "to_materials_dict", + "name": "_object_viewer", "kind": "method", - "file": "src/underworld3/model.py", - "line": 4621, - "signature": "(self) -> Dict[str, Dict[str, Any]]", + "file": "src/underworld3/constitutive_models.py", + "line": 1900, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, Dict[str, Any]]", - "existing_docstring": "Convert configuration to materials dictionary format.\n\nReturns\n-------\ndict\n Materials dictionary suitable for Model.materials", - "harvested_comments": [], - "status": "partial", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "super()._object_viewer()", + "## Viscous deformation\"))", + "### Elastic deformation\"))", + "### Plastic deformation\"))", + "# Todo: add all the other properties in here" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ThermalConvectionConfig", - "is_public": true + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false }, { - "name": "create_model", - "kind": "function", - "file": "src/underworld3/model.py", - "line": 4640, - "signature": "(name: Optional[str] = None) -> Model", - "parameters": [ - { - "name": "name", - "type_hint": "Optional[str]", - "default": "None", - "description": "" - } + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2145, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance" ], - "returns": "Model", - "existing_docstring": "Create a new Model instance", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true - }, - { - "name": "create_thermal_convection_model", - "kind": "function", - "file": "src/underworld3/model.py", - "line": 4645, - "signature": "(config: ThermalConvectionConfig, name: str = 'thermal_convection') -> Model", - "parameters": [ - { - "name": "config", - "type_hint": "ThermalConvectionConfig", - "default": null, - "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": "'thermal_convection'", - "description": "" - } - ], - "returns": "Model", - "existing_docstring": "Create a Model instance configured for thermal convection.\n\nDemonstrates integration between specialized configs and Model infrastructure.\n\nParameters\n----------\nconfig : ThermalConvectionConfig\n Configuration object with all simulation parameters\nname : str\n Model name\n\nReturns\n-------\nModel\n Configured model ready for thermal convection simulation", - "harvested_comments": [ - "Set materials from config", - "Set PETSc options from config", - "Store config in metadata for reproducibility" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "parent_class": "DiffusionModel", + "is_public": false }, { - "name": "barrier", - "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 50, - "signature": "()", + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2216, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Creates an MPI barrier. All processes wait here for others to catch up.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "AnisotropicDiffusionModel", + "is_public": false }, { - "name": "selective_ranks", - "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 160, - "signature": "(ranks)", - "parameters": [ - { - "name": "ranks", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2294, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Execute code only on selected ranks, with collective operation detection.\n\nThis context manager allows you to selectively execute code on specific MPI ranks\nwhile protecting against deadlocks from collective operations.\n\nArgs:\n ranks: Which ranks should execute the code block. Can be:\n - int: Single rank (e.g., 0)\n - slice: Range of ranks (e.g., slice(0, 4))\n - list/tuple: Specific ranks (e.g., [0, 3, 7])\n - str: Named patterns ('all', 'first', 'last', 'even', 'odd', '10%')\n - callable: Function taking rank and returning bool\n - numpy array: Boolean mask or integer indices\n\nRaises:\n CollectiveOperationError: If a collective operation is detected within\n the selective execution block (would cause deadlock)\n\nExample:\n >>> with uw.mpi.selective_ranks(0):\n ... import matplotlib.pyplot as plt\n ... plt.plot(x, y)\n ... plt.savefig(\"output.png\")", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": null, - "is_public": true - }, - { - "name": "collective_operation", - "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 215, - "signature": "(func)", - "parameters": [ - { - "name": "func", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Decorator to mark a function as a collective operation.\n\nCollective operations must be called on ALL MPI ranks. If called inside\na selective_ranks() context where not all ranks execute, raises CollectiveOperationError.\n\nExample:\n >>> @collective_operation\n ... def compute_global_stats(self):\n ... # This requires all ranks to participate\n ... return self.vec.norm()", - "harvested_comments": [ - "This requires all ranks to participate", - "Check if all ranks are executing", - "Not all ranks will execute - this is a collective operation error", - "All ranks execute\\n\"" - ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "GenericFluxModel", + "is_public": false }, { - "name": "pprint", - "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 265, - "signature": "(*args, **kwargs)", - "parameters": [ - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2427, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Parallel-safe print that works as a drop-in replacement for print().\n\nThis function ensures all ranks execute any collective operations in the arguments,\nbut only selected ranks actually print output. This prevents deadlocks from\ncollective operations inside rank conditionals.\n\nArgs:\n *args: Arguments to print (same as standard print())\n proc: Which ranks should print. Can be:\n - int: Single rank (e.g., 0) [default: 0]\n - slice: Range of ranks (e.g., slice(0, 4))\n - list/tuple: Specific ranks (e.g., [0, 3, 7])\n - str: Named patterns ('all', 'first', 'last', 'even', 'odd', '10%')\n - callable: Function taking rank and returning bool\n - numpy array: Boolean mask or integer indices\n prefix: If True, prefix output with rank number. If None (default),\n automatically enables in parallel (size > 1) and disables in serial.\n clean_display: If True, filter out SymPy uniqueness strings for cleaner display (default: True)\n flush: If True, forcibly flush the stream (default: False, same as print())\n **kwargs: Additional keyword arguments passed to print() (sep, end, file)\n\nExample:\n >>> uw.pprint(f\"Global max: {var.stats()['max']}\") # Only rank 0 prints\n Global max: 42.5\n\n >>> # In parallel, automatic prefix\n >>> uw.pprint(f\"Local max: {var.data.max()}\", proc=slice(0, 4))\n [0] Local max: 12.3\n [1] Local max: 15.7\n [2] Local max: 9.8\n [3] Local max: 11.2\n\n >>> uw.pprint(f\"Expression: {expr}\") # Automatically cleans symbols\n Expression: T(x,y)", + "existing_docstring": null, "harvested_comments": [ - "Only rank 0 prints", - "In parallel, automatic prefix", - "Automatically cleans symbols", - "Auto-detect prefix: True in parallel, False in serial", - "Clean up display strings by filtering out SymPy uniqueness patterns" + "# feedback on this instance" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "DarcyFlowModel", + "is_public": false }, { - "name": "pprint_old", - "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 341, - "signature": "(ranks, *args, **kwargs)", - "parameters": [ - { - "name": "ranks", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "*args", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**kwargs", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2635, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Legacy pprint interface (deprecated). Use pprint() with proc= parameter instead.\n\nThis function maintains backward compatibility for existing code.", + "existing_docstring": null, "harvested_comments": [ - "Convert to new interface" + "# feedback on this instance" ], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicFlowModel", + "is_public": false }, { - "name": "validate", + "name": "_object_viewer", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 65, - "signature": "(self, value: Any) -> bool", - "parameters": [ - { - "name": "value", - "type_hint": "Any", - "default": null, - "description": "" - } - ], - "returns": "bool", - "existing_docstring": "Validate a parameter value against this definition.\n\nReturns:\n--------\nbool\n True if value is valid, raises ValueError if not", - "harvested_comments": [ - "Type validation", - "Custom validation" - ], - "status": "partial", + "file": "src/underworld3/constitutive_models.py", + "line": 4089, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterDefinition", - "is_public": true + "parent_class": "MultiMaterialConstitutiveModel", + "is_public": false }, { - "name": "define_parameter", + "name": "__new__", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 148, - "signature": "(self, name: str, ptype: ParameterType, default: Any = None, bounds: Optional[tuple] = None, choices: Optional[list] = None, description: str = '', units: str = '', validator: Optional[Callable] = None, dependencies: Optional[List[str]] = None) -> None", + "file": "src/underworld3/coordinates.py", + "line": 88, + "signature": "(cls, index, system, pretty_str = None, latex_str = None, mesh = None, axis_index = None)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "index", + "type_hint": null, "default": null, "description": "" }, { - "name": "ptype", - "type_hint": "ParameterType", + "name": "system", + "type_hint": null, "default": null, "description": "" }, { - "name": "default", - "type_hint": "Any", - "default": "None", - "description": "" - }, - { - "name": "bounds", - "type_hint": "Optional[tuple]", + "name": "pretty_str", + "type_hint": null, "default": "None", "description": "" }, { - "name": "choices", - "type_hint": "Optional[list]", + "name": "latex_str", + "type_hint": null, "default": "None", "description": "" }, { - "name": "description", - "type_hint": "str", - "default": "''", - "description": "" - }, - { - "name": "units", - "type_hint": "str", - "default": "''", - "description": "" - }, - { - "name": "validator", - "type_hint": "Optional[Callable]", + "name": "mesh", + "type_hint": null, "default": "None", "description": "" }, { - "name": "dependencies", - "type_hint": "Optional[List[str]]", + "name": "axis_index", + "type_hint": null, "default": "None", "description": "" } ], - "returns": "None", - "existing_docstring": "Define a new parameter with validation rules.\n\nParameters:\n-----------\nname : str\n Parameter path (e.g., 'material.viscosity')\nptype : ParameterType\n Parameter type for validation\ndefault : Any\n Default value\nbounds : tuple, optional\n (min, max) bounds for numerical parameters\nchoices : list, optional\n Valid choices for enum parameters\ndescription : str\n Human-readable description\nunits : str\n Physical units\nvalidator : callable, optional\n Custom validation function\ndependencies : list, optional\n Parameters that depend on this one", + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "Set default value if provided" + "Create as a BaseScalar with the same index and system" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false + }, + { + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 248, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UWCoordinate", + "is_public": false + }, + { + "name": "__cinit__", + "kind": "method", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 5, + "signature": "def __cinit__(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__dealloc__", + "kind": "method", + "file": "src/underworld3/cython/petsc_types.pyx", + "line": 12, + "signature": "def __dealloc__(self):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false }, { - "name": "set_parameter", - "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 202, - "signature": "(self, name: str, value: Any, validate: bool = True) -> None", + "name": "_temporary_petsc_option", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 32, + "signature": "(key, value)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "key", + "type_hint": null, "default": null, "description": "" }, { "name": "value", - "type_hint": "Any", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "validate", - "type_hint": "bool", - "default": "True", - "description": "" } ], - "returns": "None", - "existing_docstring": "Set a parameter value with validation and dependency updates.\n\nParameters:\n-----------\nname : str\n Parameter path\nvalue : Any\n New parameter value\nvalidate : bool\n Whether to validate the value", - "harvested_comments": [ - "Get definition for validation", - "Validate value", - "Store old value for history", - "Set new value", - "Record change in history" - ], - "status": "partial", + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "get_parameter", + "name": "__del__", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 260, - "signature": "(self, name: str, default = None) -> Any", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "default", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": "Any", - "existing_docstring": "Get a parameter value by name", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3013, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "Mesh", + "is_public": false }, { - "name": "has_parameter", + "name": "_build_kd_tree_index_DS", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 264, - "signature": "(self, name: str) -> bool", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - } + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4483, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Build this from the PETScDS rather than the SWARM", + "self._index.build_index()" ], - "returns": "bool", - "existing_docstring": "Check if a parameter is defined", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "Mesh", + "is_public": false }, { - "name": "list_parameters", + "name": "_build_kd_tree_index", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 268, - "signature": "(self) -> Dict[str, Any]", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4506, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "List all parameter names and current values", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "Navigation indices build from the nav DM (a 1-cell-overlap", + "clone on manifold meshes; identical to self.dm on volume", + "meshes). Cell indices in the resulting _indexMap and", + "_centroid_index correspond to nav-DM local cell ordering.", + "Use raw internal array for KD-tree construction (avoid unit-aware wrapping)" + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "Mesh", + "is_public": false }, { - "name": "get_definition", + "name": "_build_kd_tree_index_PIC", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 272, - "signature": "(self, name: str) -> Optional[ParameterDefinition]", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - } + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4597, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# Bootstrapping - the kd-tree is needed to build the index but", + "# the index is also used in the kd-tree.", + "Create a temp swarm which we'll use to populate particles", + "at gauss points. These will then be used as basis for", + "kd-tree indexing back to owning cells." ], - "returns": "Optional[ParameterDefinition]", - "existing_docstring": "Get parameter definition by name", - "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "Mesh", + "is_public": false }, { - "name": "add_callback", + "name": "_get_domain_centroids", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 276, - "signature": "(self, name: str, callback: Callable) -> None", - "parameters": [ - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "callback", - "type_hint": "Callable", - "default": null, - "description": "" - } - ], - "returns": "None", - "existing_docstring": "Add a callback function to be called when parameter changes.\n\nParameters:\n-----------\nname : str\n Parameter path\ncallback : callable\n Function called as callback(param_name, new_value, old_value)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5554, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "Mesh", + "is_public": false }, { - "name": "export_config", + "name": "_get_domain_kdtree", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 291, - "signature": "(self) -> Dict[str, Any]", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5563, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "Export current parameter configuration", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "Mesh", + "is_public": false }, { - "name": "import_config", + "name": "_data_layout", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 308, - "signature": "(self, config: Dict[str, Any], validate: bool = True) -> None", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1654, + "signature": "(self, i, j = None)", "parameters": [ { - "name": "config", - "type_hint": "Dict[str, Any]", + "name": "i", + "type_hint": null, "default": null, "description": "" }, { - "name": "validate", - "type_hint": "bool", - "default": "True", + "name": "j", + "type_hint": null, + "default": "None", "description": "" } ], - "returns": "None", - "existing_docstring": "Import parameter configuration from exported dict", + "returns": null, + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", - "is_public": true + "parent_class": "_BaseMeshVariable", + "is_public": false }, { - "name": "define_thermal_convection_parameters", - "kind": "function", - "file": "src/underworld3/parameters.py", - "line": 322, - "signature": "(registry: ParameterRegistry) -> None", - "parameters": [ - { - "name": "registry", - "type_hint": "ParameterRegistry", - "default": null, - "description": "" - } + "name": "_setup_ds", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1699, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "self.clean_name ## Filling up the options database", + "only active if discontinuous", + "Check if this is the first field or if we need to rebuild the DM", + "(needed to ensure Section is properly synchronized with field list)", + "DM already has fields - need to rebuild to sync Section" ], - "returns": "None", - "existing_docstring": "Define standard parameters for thermal convection models", - "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "_BaseMeshVariable", + "is_public": false }, { - "name": "define_stokes_flow_parameters", - "kind": "function", - "file": "src/underworld3/parameters.py", - "line": 379, - "signature": "(registry: ParameterRegistry) -> None", + "name": "_set_vec", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1796, + "signature": "(self, available)", "parameters": [ { - "name": "registry", - "type_hint": "ParameterRegistry", + "name": "available", + "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Define standard parameters for Stokes flow models", - "harvested_comments": [], - "status": "partial", + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "not sure if required, but to be sure.", + "This is set for checkpointing." + ], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "_BaseMeshVariable", + "is_public": false }, { - "name": "units", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 360, + "name": "__del__", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1890, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Return the units associated with this variable.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": "_BaseMeshVariable", + "is_public": false }, { - "name": "units", + "name": "_remesh_managed_by", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 365, + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 394, "signature": "(self, value)", "parameters": [ { @@ -23991,8498 +49306,8045 @@ } ], "returns": null, - "existing_docstring": "Set the units for this variable.", - "harvested_comments": [ - "Convert string units to Pint Unit objects for consistency" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": "EnhancedMeshVariable", + "is_public": false }, { - "name": "has_units", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 374, - "signature": "(self)", + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/function/_dminterp_wrapper.pyx", + "line": 234, + "signature": "def __repr__(self):", "parameters": [], "returns": null, - "existing_docstring": "Check if this variable has units.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "use_legacy_array", + "name": "_latex", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 936, - "signature": "(self)", + "file": "src/underworld3/function/_function.pyx", + "line": 91, + "signature": "def _latex(self, printer, exp=None):", "parameters": [], "returns": null, - "existing_docstring": "Deprecated: Array interface is now unified using NDArray_With_Callback", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "use_enhanced_array", + "name": "__new__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 940, - "signature": "(self)", + "file": "src/underworld3/function/_function.pyx", + "line": 170, + "signature": "def __new__(cls,\n name : str,\n meshvar : underworld3.discretisation.MeshVariable,\n vtype : underworld3.VarType,\n component: Union[int, tuple] = 0,\n data_loc: int = None,\n *args, **options):", "parameters": [], "returns": null, - "existing_docstring": "Deprecated: Array interface is now unified using NDArray_With_Callback", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "sync_disabled", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 944, - "signature": "(self, description = 'batch operation')", - "parameters": [ - { - "name": "description", - "type_hint": null, - "default": "'batch operation'", - "description": "" - } - ], + "name": "_dmswarm_get_migrate_type", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 1550, + "signature": "def _dmswarm_get_migrate_type(sdm):", + "parameters": [], "returns": null, - "existing_docstring": "Context manager to disable automatic synchronization for batch operations.\nNow uses NDArray_With_Callback's delay_callback mechanism.\n\nParameters\n----------\ndescription : str\n Description of the batch operation for debugging", - "harvested_comments": [ - "Use NDArray_With_Callback's built-in delay mechanism" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "partial", + "parent_class": null, + "is_public": false + }, + { + "name": "_dmswarm_set_migrate_type", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 1560, + "signature": "def _dmswarm_set_migrate_type(sdm, mtype:PETsc.DMSwarm.MigrateType):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "pack_uw_data_to_petsc", + "name": "_ccode", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1157, - "signature": "(self, data_array, sync = True)", - "parameters": [ - { - "name": "data_array", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "sync", - "type_hint": null, - "default": "True", - "description": "" - } - ], + "file": "src/underworld3/function/analytic.pyx", + "line": 38, + "signature": "def _ccode(self, printer):", + "parameters": [], "returns": null, - "existing_docstring": "Enhanced pack method that directly accesses PETSc field without access() context.\nDesigned for the new swarmVariable.array interface.\n\nParameters\n----------\ndata_array : numpy.ndarray\n Array data to pack into PETSc field\nsync : bool\n Whether to sync parallel operations (default True)", - "harvested_comments": [ - "Direct PETSc field access without context manager", - "Pack data using same layout as original method", - "Increment variable state to track changes", - "Update the proxy mesh variable if one exists (for integral calculations)", - "Sync parallel operations if requested" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "unpack_uw_data_from_petsc", + "name": "_eval_evalf", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1220, - "signature": "(self, squeeze = True, sync = True)", - "parameters": [ - { - "name": "squeeze", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "sync", - "type_hint": null, - "default": "True", - "description": "" - } - ], + "file": "src/underworld3/function/analytic.pyx", + "line": 58, + "signature": "def _eval_evalf(self,prec):", + "parameters": [], "returns": null, - "existing_docstring": "Enhanced unpack method that directly accesses PETSc field without access() context.\nDesigned for the new swarmVariable.array interface.\n\nParameters\n----------\nsqueeze : bool\n Whether to squeeze singleton dimensions (default True)\nsync : bool\n Whether to sync parallel operations (default True)", - "harvested_comments": [ - "Direct PETSc field access without context manager", - "Sync parallel operations if requested", - "TODO: Add parallel sync logic here if needed", - "Unpack data using same layout as original method", - "Always restore the field" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "pack_raw_data_to_petsc", + "name": "_eval_evalf", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1261, - "signature": "(self, data_array, sync = True)", - "parameters": [ - { - "name": "data_array", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "sync", - "type_hint": null, - "default": "True", - "description": "" - } - ], + "file": "src/underworld3/function/analytic.pyx", + "line": 63, + "signature": "def _eval_evalf(self,prec):", + "parameters": [], "returns": null, - "existing_docstring": "Pack data array to PETSc using traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\ndata_array : numpy.ndarray\n Array data in traditional flat format (-1, num_components)\nsync : bool\n Whether to sync parallel operations (default True)", - "harvested_comments": [ - "Convert to expected shape: (-1, num_components)", - "Direct PETSc field access without context manager", - "Direct assignment in traditional flat format", - "Increment variable state to track changes", - "Update the proxy mesh variable if one exists (for integral calculations)" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "unpack_raw_data_from_petsc", + "name": "_eval_evalf", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1306, - "signature": "(self, squeeze = True, sync = True)", - "parameters": [ - { - "name": "squeeze", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "sync", - "type_hint": null, - "default": "True", - "description": "" - } - ], + "file": "src/underworld3/function/analytic.pyx", + "line": 76, + "signature": "def _eval_evalf(self,prec):", + "parameters": [], "returns": null, - "existing_docstring": "Unpack data from PETSc in traditional data shape (-1, num_components).\nDirect PETSc access without access() context for backward compatibility.\n\nParameters\n----------\nsqueeze : bool\n Whether to remove singleton dimensions (default True)\nsync : bool\n Whether to sync parallel operations (default True)\n\nReturns\n-------\nnumpy.ndarray\n Array data in traditional flat format (-1, num_components)", - "harvested_comments": [ - "Check if swarm has any particles before accessing field", - "Swarm not populated yet, return empty array", - "Direct PETSc field access without context manager", - "Field not properly initialized, restore and return empty array", - "Return data in traditional flat format" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "rbf_interpolate", + "name": "_eval_evalf", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1380, - "signature": "(self, new_coords, verbose = False, nnn = None)", - "parameters": [ - { - "name": "new_coords", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "nnn", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/function/analytic.pyx", + "line": 81, + "signature": "def _eval_evalf(self,prec):", + "parameters": [], "returns": null, - "existing_docstring": "Radial basis function interpolation of particle data to arbitrary points.\n\nUses inverse-distance weighting to interpolate particle values\nto new coordinate locations.\n\nParameters\n----------\nnew_coords : numpy.ndarray\n Target coordinates of shape (N, dim) to interpolate to.\nverbose : bool, default=False\n Print diagnostic information during interpolation.\nnnn : int, optional\n Number of nearest neighbors to use. Defaults to ``mesh.dim + 1``.\n\nReturns\n-------\nnumpy.ndarray\n Interpolated values at the target coordinates.", - "harvested_comments": [ - "An inverse-distance mapping is quite robust here ... as long", - "as we take care of the case where some nodes coincide (likely if used with mesh2mesh)", - "We try to eliminate contributions from recently remeshed particles", - "Get data directly from PETSc to avoid circular callback dependencies", - "What to do if there are no particles" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "swarm", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 1440, - "signature": "(self)", + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 94, + "signature": "def _eval_evalf(self,prec):", "parameters": [], "returns": null, - "existing_docstring": "The swarm this variable belongs to (accessed via weak reference).\nRaises RuntimeError if the swarm has been garbage collected.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "old_data", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 1457, - "signature": "(self)", + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 111, + "signature": "def _eval_evalf(self,prec):", "parameters": [], "returns": null, - "existing_docstring": "TESTING: Original data property implementation.", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "data", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 1464, - "signature": "(self)", + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 116, + "signature": "def _eval_evalf(self,prec):", "parameters": [], "returns": null, - "existing_docstring": "Canonical data storage with PETSc synchronization.\nShape: (-1, num_components) - flat format for backward compatibility.\n\nThis is the ONLY property that handles PETSc synchronization to avoid conflicts.\nThe .array property uses this as its underlying storage with format conversion.\n\nReturns\n-------\nNDArray_With_Callback\n Array with shape (-1, num_components) with automatic PETSc synchronization", - "harvested_comments": [ - "Cache and reuse canonical data object to avoid field access conflicts", - "Use direct __dict__ check to avoid MathematicalMixin recursion", - "Create the single canonical data array with PETSc sync" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "array", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 1486, - "signature": "(self)", + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 129, + "signature": "def _eval_evalf(self,prec):", "parameters": [], "returns": null, - "existing_docstring": "Array view of canonical data with automatic format conversion.\nShape: (N, a, b) for tensor shape (a, b).\n\nThis property is ALWAYS a view of the canonical .data property.\nNo direct PETSc access - all changes delegate back to canonical storage.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "array", + "name": "_eval_evalf", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1497, - "signature": "(self, array_value)", - "parameters": [ - { - "name": "array_value", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/function/analytic.pyx", + "line": 135, + "signature": "def _eval_evalf(self,prec):", + "parameters": [], "returns": null, - "existing_docstring": "Set variable data through canonical data property with format conversion.", - "harvested_comments": [ - "Simple case: reshape array format (N,a,b) to canonical format (N,components)", - "Complex case: use pack operations for tensor layout conversion", - "Assign to canonical data property (triggers PETSc sync)" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "sym", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 1512, - "signature": "(self)", + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 144, + "signature": "def _eval_evalf(self,prec):", "parameters": [], "returns": null, - "existing_docstring": "Symbolic representation for use in equations.\n\nReturns the symbolic expression from the proxy mesh variable,\nwhich can be used in SymPy expressions for constitutive models,\nboundary conditions, etc.\n\nReturns\n-------\nsympy.Matrix\n Symbolic matrix expression.\n\nNotes\n-----\nThe proxy is automatically updated if particle data has changed.", - "harvested_comments": [ - "Ensure proxy is up to date before returning symbolic representation" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "sym_1d", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 1533, - "signature": "(self)", + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 149, + "signature": "def _eval_evalf(self,prec):", "parameters": [], "returns": null, - "existing_docstring": "Flattened symbolic representation.\n\nReturns the symbolic expression as a 1D (column) vector form,\nuseful for Voigt notation in tensor calculations.\n\nReturns\n-------\nsympy.Matrix\n Flattened symbolic expression.", - "harvested_comments": [ - "Ensure proxy is up to date before returning symbolic representation" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "partial", + "parent_class": null, + "is_public": false + }, + { + "name": "_eval_evalf", + "kind": "method", + "file": "src/underworld3/function/analytic.pyx", + "line": 154, + "signature": "def _eval_evalf(self,prec):", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "global_max", + "name": "__new__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1554, - "signature": "(self, axis = None, out = None, keepdims = False)", + "file": "src/underworld3/function/expressions.py", + "line": 667, + "signature": "(cls, name, *args, **kwargs)", "parameters": [ { - "name": "axis", + "name": "name", "type_hint": null, - "default": "None", + "default": null, "description": "" }, { - "name": "out", + "name": "*args", "type_hint": null, - "default": "None", + "default": null, "description": "" }, { - "name": "keepdims", + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Maximum value across all MPI ranks.\n\nFinds the maximum value of the particle property across all processors.\nUseful for finding extreme values in particle swarm data.\n\nParameters\n----------\naxis : None, int, or tuple of ints, optional\n Axis or axes along which to operate. By default, flattened input is used.\nout : None, optional\n Alternative output array (not supported, kept for API compatibility).\nkeepdims : bool, optional\n If True, reduced axes are left as dimensions with size one.\n\nReturns\n-------\nUWQuantity or scalar\n Maximum value with units preserved (if variable has units).\n\nExamples\n--------\n>>> max_temp = temperature_swarm.global_max()\n>>> print(f\"Maximum temperature: {max_temp}\")\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.", + "existing_docstring": null, "harvested_comments": [ - "Wrap data in UnitAwareArray to use its global_max implementation" + "If the expression already exists, return it", + "Check both dicts for name collisions", + "Check ephemeral dict - need to look for any key starting with this name.", + "Snapshot keys via list(...) before iterating: the underlying dict can", + "be mutated mid-iteration by weakref finalizers running asynchronously" ], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false }, { - "name": "global_min", + "name": "__lt__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1592, - "signature": "(self, axis = None, out = None, keepdims = False)", + "file": "src/underworld3/function/quantities.py", + "line": 730, + "signature": "(self, other)", "parameters": [ { - "name": "axis", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "out", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "keepdims", + "name": "other", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Minimum value across all MPI ranks.\n\nFinds the minimum value of the particle property across all processors.\nUseful for finding extreme values in particle swarm data.\n\nParameters\n----------\naxis : None, int, or tuple of ints, optional\n Axis or axes along which to operate. By default, flattened input is used.\nout : None, optional\n Alternative output array (not supported, kept for API compatibility).\nkeepdims : bool, optional\n If True, reduced axes are left as dimensions with size one.\n\nReturns\n-------\nUWQuantity or scalar\n Minimum value with units preserved (if variable has units).\n\nExamples\n--------\n>>> min_pressure = pressure_swarm.global_min()\n>>> print(f\"Minimum pressure: {min_pressure}\")\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.", + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "global_sum", + "name": "__le__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1629, - "signature": "(self, axis = None, out = None, keepdims = False)", + "file": "src/underworld3/function/quantities.py", + "line": 737, + "signature": "(self, other)", "parameters": [ { - "name": "axis", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "out", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "keepdims", + "name": "other", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Sum of values across all MPI ranks.\n\nComputes the sum of particle property values across all processors.\n\nParameters\n----------\naxis : None, int, or tuple of ints, optional\n Axis or axes along which to operate. By default, flattened input is used.\nout : None, optional\n Alternative output array (not supported, kept for API compatibility).\nkeepdims : bool, optional\n If True, reduced axes are left as dimensions with size one.\n\nReturns\n-------\nUWQuantity or scalar\n Sum with units preserved (if variable has units).\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.\n\nWarning: This sum is NOT a physical domain-integrated quantity because\nparticles are non-uniformly distributed. For domain integration, use\nthe proxy mesh variable with uw.maths.Integral().", + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "global_norm", + "name": "__gt__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1664, - "signature": "(self, ord = None)", + "file": "src/underworld3/function/quantities.py", + "line": 744, + "signature": "(self, other)", "parameters": [ { - "name": "ord", + "name": "other", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "L2 norm (Frobenius norm) across all MPI ranks.\n\nComputes the L2 norm of particle property values: sqrt(sum(x**2))\nacross all processors.\n\nParameters\n----------\nord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional\n Order of the norm (default: None = 2-norm)\n\nReturns\n-------\nUWQuantity or scalar\n L2 norm with units preserved (if variable has units).\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.\n\nFor vectors, computes the Frobenius norm treating the array as flattened.\n\nWarning: This norm is NOT a physical domain-integrated quantity because\nparticles are non-uniformly distributed.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": true - }, - { - "name": "global_size", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1697, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Total particle count across all MPI ranks.\n\nReturns the total number of particles across all processors.\nUseful for population monitoring and load balancing diagnostics.\n\nReturns\n-------\nint\n Total number of particles across all ranks.\n\nExamples\n--------\n>>> total_particles = swarm_var.global_size()\n>>> local_particles = swarm_var.data.shape[0]\n>>> print(f\"Rank has {local_particles} of {total_particles} particles\")\n\nNotes\n-----\nThis is a collective operation - all ranks must call it.\nThe result is identical on all ranks.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", - "is_public": true + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "save", + "name": "__ge__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1726, - "signature": "(self, filename: int, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential = False)", + "file": "src/underworld3/function/quantities.py", + "line": 751, + "signature": "(self, other)", "parameters": [ { - "name": "filename", - "type_hint": "int", - "default": null, - "description": "" - }, - { - "name": "compression", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "compressionType", - "type_hint": "Optional[str]", - "default": "'gzip'", - "description": "" - }, - { - "name": "force_sequential", + "name": "other", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Save the swarm variable to a h5 file.\n\nParameters\n----------\nfilename :\n The filename of the swarm variable to save to disk.\ncompression :\n Add compression to the h5 files (saves space but increases write times with increasing no. of processors)\ncompressionType :\n Type of compression to use, 'gzip' and 'lzf' supported. 'gzip' is default. Compression also needs to be set to 'True'.\n\nforce_sequential : activate the serial version of hdf5", - "harvested_comments": [ - "# Add swarm variable unit metadata to the file", - "Use preferred selective_ranks pattern for unit metadata", - "Add swarm variable unit metadata", - "Store in dataset attributes" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "SwarmVariable", - "is_public": true - }, - { - "name": "sym", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2029, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Lazy evaluation of symbolic mask array.\n\nOnly updates proxy variables when they're actually needed (when sym is accessed)\nand only if the proxy variables are marked as stale due to data changes.\nThis avoids expensive RBF interpolation during data assignment operations.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", - "is_public": true + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "createMask", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2051, - "signature": "(self, funcsList)", + "name": "__eq__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 758, + "signature": "(self, other)", "parameters": [ { - "name": "funcsList", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "This creates a masked sympy function of swarm variables required for Underworld's solvers", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", - "is_public": true + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "viewMask", + "name": "__ne__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2068, - "signature": "(self, sympy)", + "file": "src/underworld3/function/quantities.py", + "line": 765, + "signature": "(self, other)", "parameters": [ { - "name": "sympy", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Takes a previously masked sympy function and returns individual sympy objects corresponding to each material", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", - "is_public": true + "parent_class": "UWQuantity", + "is_public": false }, { - "name": "view", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2088, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Show information on IndexSwarmVariable", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "IndexSwarmVariable", - "is_public": true - }, - { - "name": "mesh", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2425, + "file": "src/underworld3/materials.py", + "line": 339, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "The mesh this swarm operates on", + "existing_docstring": null, "harvested_comments": [], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "MaterialRegistry", + "is_public": false }, { - "name": "mesh", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2433, - "signature": "(self, new_mesh)", + "name": "_unit", + "kind": "function", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 25, + "signature": "(v)", "parameters": [ { - "name": "new_mesh", + "name": "v", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Assign swarm to a new mesh with dimensional validation and proxy updates.\n\nParameters\n----------\nnew_mesh : uw.discretisation.Mesh\n New mesh to assign this swarm to\n\nRaises\n------\nValueError\n If new mesh has incompatible dimensions", - "harvested_comments": [ - "Register new mesh with model", - "Check if swarm is already compatible with target mesh", - "Dimensions match, check if proxy variables need updating", - "No change needed", - "Use swarm's current dimensions for validation (not model.mesh which may have been auto-updated)" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "local_size", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2508, + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 202, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Number of particles on this MPI rank.\n\nReturns\n-------\nint\n Local particle count.\n\nSee Also\n--------\ndm.getLocalSize : Underlying PETSc method.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "BoundingSurface", + "is_public": false }, { - "name": "data", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2525, - "signature": "(self)", + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 364, + "signature": "(self) -> str", "parameters": [], - "returns": null, - "existing_docstring": "Particle coordinates (alias for :attr:`points`).\n\n.. deprecated:: 0.99.0\n Use direct DM field access for particle coordinates.\n\nReturns\n-------\nnumpy.ndarray\n Particle coordinate array of shape ``(n_particles, dim)``.", + "returns": "str", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "FaultSurface", + "is_public": false }, { - "name": "points", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2539, - "signature": "(self)", + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/meshing/faults.py", + "line": 660, + "signature": "(self) -> str", "parameters": [], - "returns": null, - "existing_docstring": "Swarm particle coordinates in physical units.\n\n.. deprecated:: 0.99.0\n Use swarm variables or direct DM access instead.\n ``swarm.points`` is being deprecated.\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from internal model coordinates\nto physical coordinates for user access.\n\nWhen the mesh has coordinate units specified, returns a unit-aware array.\n\nReturns:\n numpy.ndarray or UnitAwareArray: Particle coordinates (with units if mesh.units is set)", - "harvested_comments": [ - "Check for mesh coordinate changes and trigger migration if needed", - "Mesh coordinates have changed, force migration to update swarm", - "Update our mesh version to match", - "Get current coordinate data from PETSc (these are in model coordinates)", - "Apply scaling to convert model coordinates to physical coordinates" - ], - "status": "partial", + "returns": "str", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "FaultCollection", + "is_public": false }, { - "name": "points", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2650, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Set swarm particle coordinates from physical units.\n\n.. deprecated:: 0.99.0\n Use swarm variables or direct DM access instead.\n\nWhen the mesh has coordinate scaling applied (via model units),\nthis property automatically converts from physical coordinates\nto internal model coordinates for PETSc storage.\n\nArgs:\n value (numpy.ndarray): Particle coordinates in physical units", - "harvested_comments": [ - "Apply inverse scaling to convert physical coordinates to model coordinates", - "Update the cached NDArray (triggers callback) - use physical coordinates for cache", - "Update PETSc DM field directly with model coordinates for immediate consistency" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "Swarm", - "is_public": true - }, - { - "name": "coords", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2709, - "signature": "(self)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 595, + "signature": "(self) -> str", "parameters": [], - "returns": null, - "existing_docstring": "Swarm particle coordinates in physical units.\n\nThis is the primary public interface for accessing particle coordinates.\nCoordinates are automatically converted from internal model units to\nphysical units based on the model's reference quantities.\n\nReturns\n-------\nUWQuantity or numpy.ndarray\n Particle coordinates in physical units with shape (n_particles, dim).\n If model has reference quantities, returns UWQuantity with appropriate\n length units. Otherwise returns plain array.\n\nNotes\n-----\n- Coordinates are converted from model units to physical units automatically\n- For internal use with model units, access `swarm._particle_coordinates.data`\n- Setting coordinates accepts either physical units or plain numbers\n\nExamples\n--------\n>>> coords_physical = swarm.coords # Get physical coordinates\n>>> swarm.coords = new_coords_with_units # Set from physical units\n\nSee Also\n--------\nswarm.units : Get the unit specification for coordinates", + "returns": "str", + "existing_docstring": null, "harvested_comments": [ - "Get physical coordinates", - "Set from physical units", - "Get internal model-unit coordinates", - "Convert to physical units", - "Use from_model_magnitude to convert back to physical" + "Get raw data length to avoid triggering UnitAwareArray" ], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "SurfaceVariable", + "is_public": false }, { - "name": "coords", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2751, - "signature": "(self, value)", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": null, - "existing_docstring": "Set swarm particle coordinates from physical units.\n\nAccepts coordinates with units or plain numbers. If units are provided,\nthey are converted to model units automatically. If plain numbers are\nprovided, they are assumed to be in the correct unit system.\n\nParameters\n----------\nvalue : array-like or UWQuantity\n New coordinates. Can be:\n - Array with units (e.g., values * uw.units.km)\n - Plain array (assumed to be in model units or physical units depending on context)", - "harvested_comments": [ - "Convert physical \u2192 non-dimensional units", - "Set internal coordinates" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "Swarm", - "is_public": true - }, - { - "name": "units", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2775, - "signature": "(self)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1937, + "signature": "(self) -> str", "parameters": [], - "returns": null, - "existing_docstring": "Unit specification for swarm coordinates.\n\nReturns the physical unit string for coordinates based on the model's\nreference quantities. This indicates what units the coordinates are in\nwhen accessed via the `coords` property.\n\nReturns\n-------\nstr or None\n Unit string for coordinates (e.g., 'kilometer', 'meter'), or None\n if no reference quantities are set\n\nExamples\n--------\n>>> print(swarm.units) # 'kilometer' if length_scale was set in km\n>>> coords = swarm.coords # Coordinates in kilometers", - "harvested_comments": [ - "'kilometer' if length_scale was set in km", - "Coordinates in kilometers", - "Coordinates have length dimensions", - "Check if model has reference quantities", - "Get length scale from model" - ], - "status": "partial", + "returns": "str", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "Surface", + "is_public": false }, { - "name": "dont_clip_to_mesh", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2825, - "signature": "(self)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2412, + "signature": "(self) -> str", "parameters": [], - "returns": null, - "existing_docstring": "Context manager that temporarily disables mesh clipping for the swarm.\n`swarm.migrate` is called automatically when exiting the context.\n\nUsage:\n with swarm.dont_clip_to_mesh():\n # swarm operations that should not be clipped to mesh\n swarm.data = new_positions", - "harvested_comments": [ - "swarm operations that should not be clipped to mesh" - ], - "status": "partial", + "returns": "str", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "SurfaceCollection", + "is_public": false }, { - "name": "migration_disabled", + "name": "__enter__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2853, + "file": "src/underworld3/mpi.py", + "line": 396, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Legacy context manager that completely disables migration.\nUse migration_control(disable=True) for new code.\n\nContext manager that temporarily disables particle migration for the swarm.\nMigration is NOT called when exiting the context.\n\nUsage:\n with swarm.migration_disabled():\n # swarm operations that should not trigger migration\n swarm.data = new_positions\n # ... other operations ...\n # migrate() will be skipped during these operations", - "harvested_comments": [ - "swarm operations that should not trigger migration", - "... other operations ...", - "migrate() will be skipped during these operations" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "call_pattern", + "is_public": false }, { - "name": "migration_control", + "name": "__exit__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2871, - "signature": "(self, disable = False)", + "file": "src/underworld3/mpi.py", + "line": 402, + "signature": "(self, *args)", "parameters": [ { - "name": "disable", + "name": "*args", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Context manager to control particle migration behavior.\n\nParameters\n----------\ndisable : bool\n If False (default), migration is deferred until context exit.\n If True, migration is completely disabled.\n\nUsage:\n # Defer migration until end (default)\n with swarm.migration_control():\n swarm.points[mask1] += delta1\n swarm.points[mask2] *= scale\n # Migration happens HERE on exit\n\n # Completely disable migration\n with swarm.migration_control(disable=True):\n # Operations where migration should never happen\n # No migration on exit", - "harvested_comments": [ - "Defer migration until end (default)", - "Migration happens HERE on exit", - "Completely disable migration", - "Operations where migration should never happen", - "No migration on exit" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "call_pattern", + "is_public": false }, { - "name": "populate", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2923, - "signature": "(self, fill_param: Optional[int] = 1)", - "parameters": [ - { - "name": "fill_param", - "type_hint": "Optional[int]", - "default": "1", - "description": "" - } - ], + "file": "src/underworld3/parameters.py", + "line": 317, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Populate the swarm with particles throughout the domain.\n\nParameters\n----------\nfill_param:\n Parameter determining the particle count per cell (per dimension)\n for the given layout, using the mesh degree.\n\nRaises\n------\nRuntimeError\n If the swarm has already been initialized with particles.", - "harvested_comments": [ - "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero", - "It allocates N-1 instead of N, so we add +1 to compensate", - "PETSc 3.24+ fixed this bug, so we use the exact count", - "This is a mesh-local quantity, so let's just", - "store it on the mesh in an ad_hoc fashion for now" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "ParameterRegistry", + "is_public": false }, { - "name": "migrate", + "name": "_process_args", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3022, - "signature": "(self, remove_sent_points = True, delete_lost_points = None, max_its = 10)", + "file": "src/underworld3/scaling/_utils.py", + "line": 28, + "signature": "(mapping = (), **kwargs)", "parameters": [ { - "name": "remove_sent_points", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "delete_lost_points", + "name": "mapping", "type_hint": null, - "default": "None", + "default": "()", "description": "" }, { - "name": "max_its", + "name": "**kwargs", "type_hint": null, - "default": "10", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Migrate swarm across processes after coordinates have been updated.\n\nThe algorithm uses a global kD-tree for the centroids of the domains to decide the particle mpi.rank (send to the closest)\nIf the particles are mis-assigned to a particular mpi.rank, the next choice is the second-closest and so on.\n\nA few particles are still not found after this distribution process which probably means they are just outside the mesh.\nIf some points remain lost, they will be deleted if `delete_lost_points` is set.\n\nImplementation note:\n We retained (above) the name `DMSwarmPIC_coor` for the particle field to allow this routine to be inherited by a PIC swarm\n which has this field pre-defined. (We'd need to add a cellid field as well, and re-compute it upon landing)\n\nNote: This is a COLLECTIVE operation - all MPI ranks must call it.", - "harvested_comments": [ - "This will only worry about particles that are not already claimed !", - "Unlikely, but we should check this", - "Migrate particles between processes (if there are more than one of them)", - "Send unclaimed points to next processor in line", - "Now we send the points (basic migration)" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "TransformedDict", + "is_public": false }, { - "name": "add_particles_with_coordinates", + "name": "__getitem__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3194, - "signature": "(self, coordinatesArray) -> int", + "file": "src/underworld3/scaling/_utils.py", + "line": 39, + "signature": "(self, k)", "parameters": [ { - "name": "coordinatesArray", + "name": "k", "type_hint": null, "default": null, "description": "" } ], - "returns": "int", - "existing_docstring": "Add particles to the swarm using particle coordinates provided\nusing a numpy array.\n\nNote that particles with coordinates NOT local to the current processor will\nbe rejected / ignored.\n\nEither include an array with all coordinates to all processors\nor an array with the local coordinates.\n\nParameters\n----------\ncoordinatesArray : numpy.ndarray\n The numpy array containing the coordinate of the new particles. Array is\n expected to take shape n*dim, where n is the number of new particles, and\n dim is the dimensionality of the swarm's supporting mesh.\n\nReturns\n--------\nnpoints: int\n The number of points added to the local section of the swarm.", - "harvested_comments": [ - "### petsc appears to ignore columns that are greater than the mesh dim, but still worth including", - "-1 means no particles have been added yet (PETSc interface change)", - "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero", - "It allocates N-1 instead of N, so we add +1 to compensate", - "PETSc 3.24+ fixed this bug, so we use the exact count" + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Swarm", - "is_public": true + "parent_class": "TransformedDict", + "is_public": false }, { - "name": "add_particles_with_global_coordinates", + "name": "__setitem__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3269, - "signature": "(self, globalCoordinatesArray, migrate = True, delete_lost_points = True) -> int", + "file": "src/underworld3/scaling/_utils.py", + "line": 42, + "signature": "(self, k, v)", "parameters": [ { - "name": "globalCoordinatesArray", + "name": "k", "type_hint": null, "default": null, "description": "" }, { - "name": "migrate", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "delete_lost_points", + "name": "v", "type_hint": null, - "default": "True", + "default": null, "description": "" } ], - "returns": "int", - "existing_docstring": "Add particles to the swarm using particle coordinates provided\nusing a numpy array.\n\nglobal coordinates: particles will be appropriately migrated\n\nParameters\n----------\nglobalCoordinatesArray : numpy.ndarray\n The numpy array containing the coordinate of the new particles. Array is\n expected to take shape n*dim, where n is the number of new particles, and\n dim is the dimensionality of the swarm's supporting mesh.\n\nReturns\n--------\nnpoints: int\n The number of points added to the local section of the swarm.", - "harvested_comments": [ - "### petsc appears to ignore columns that are greater than the mesh dim, but still worth including", - "-1 means no particles have been added yet", - "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero", - "It allocates N-1 instead of N, so we add +1 to compensate", - "PETSc 3.24+ fixed this bug, so we use the exact count" + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Swarm", - "is_public": true + "parent_class": "TransformedDict", + "is_public": false }, { - "name": "save", + "name": "__delitem__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3347, - "signature": "(self, filename: int, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential = False)", + "file": "src/underworld3/scaling/_utils.py", + "line": 45, + "signature": "(self, k)", "parameters": [ { - "name": "filename", - "type_hint": "int", - "default": null, - "description": "" - }, - { - "name": "compression", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "compressionType", - "type_hint": "Optional[str]", - "default": "'gzip'", - "description": "" - }, - { - "name": "force_sequential", + "name": "k", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Save the swarm coordinates to a h5 file.\n\nParameters\n----------\nfilename :\n The filename of the swarm checkpoint file to save to disk.\ncompression :\n Add compression to the h5 files (saves space but increases write times with increasing no. of processors)\ncompressionType :\n Type of compression to use, 'gzip' and 'lzf' supported. 'gzip' is default. Compression also needs to be set to 'True'.", - "harvested_comments": [ - "It seems to be a bad idea to mix mpi barriers with the access", - "context manager so the copy-free version of this seems to hang", - "when there are many active cores. This is probably why the parallel", - "h5py write hangs", - "It seems to be a bad idea to mix mpi barriers with the access" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "TransformedDict", + "is_public": false }, { - "name": "add_variable", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3498, - "signature": "(self, name, size = 1, dtype = float, proxy_degree = 2, _nn_proxy = False, units = None)", - "parameters": [ - { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "size", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "dtype", - "type_hint": null, - "default": "float", - "description": "" - }, - { - "name": "proxy_degree", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "_nn_proxy", - "type_hint": null, - "default": "False", - "description": "" - }, + "name": "__contains__", + "kind": "method", + "file": "src/underworld3/scaling/_utils.py", + "line": 62, + "signature": "(self, k)", + "parameters": [ { - "name": "units", + "name": "k", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Add a variable to the swarm.\n\nVariables must be created before the swarm is populated with particles.\nOnce swarm.populate() or similar methods are called, PETSc finalizes\nfield registration and no new variables can be added.\n\nParameters\n----------\nname : str\n Variable name\nsize : int, default 1\n Number of components (1 for scalar, 2-3 for vector, etc.)\ndtype : type, default float\n Data type (float or int)\nproxy_degree : int, default 2\n Degree for mesh proxy variable interpolation\n_nn_proxy : bool, default False\n Internal parameter for nearest-neighbor proxy\nunits : str, optional\n Physical units for this variable (e.g., \"kg/m^3\", \"m/s\")\n\nReturns\n-------\nSwarmVariable\n The created swarm variable\n\nRaises\n------\nRuntimeError\n If swarm is already populated with particles\n\nExamples\n--------\nCorrect usage:\n>>> swarm = uw.swarm.Swarm(mesh)\n>>> material = swarm.add_variable(\"material\", 1, dtype=int)\n>>> temperature = swarm.add_variable(\"temperature\", 1)\n>>> swarm.populate(fill_param=3) # Populate after creating variables\n\nIncorrect usage (will raise error):\n>>> swarm = uw.swarm.Swarm(mesh)\n>>> swarm.populate(fill_param=3)\n>>> material = swarm.add_variable(\"material\", 1) # ERROR!", - "harvested_comments": [ - "Populate after creating variables", - "Check early to provide a clear error message", - "Create variables first\\n\"", - "Then populate with particles\"" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Swarm", - "is_public": true + "parent_class": "TransformedDict", + "is_public": false }, { - "name": "petsc_save_checkpoint", + "name": "_repr_html_", + "kind": "method", + "file": "src/underworld3/scaling/_utils.py", + "line": 72, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "TransformedDict", + "is_public": false + }, + { + "name": "_data_layout", "kind": "method", "file": "src/underworld3/swarm.py", - "line": 3575, - "signature": "(self, swarmName: str, index: int, outputPath: Optional[str] = '')", + "line": 1009, + "signature": "(self, i, j = None)", "parameters": [ { - "name": "swarmName", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "index", - "type_hint": "int", + "name": "i", + "type_hint": null, "default": null, "description": "" }, { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", + "name": "j", + "type_hint": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Use PETSc to save the swarm and attached data to a .pbin and xdmf file.\n\nParameters\n----------\nswarmName :\n Name of the swarm to save.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "SwarmVariable", + "is_public": false }, { - "name": "write_timestep", + "name": "_create_proxy_variable", "kind": "method", "file": "src/underworld3/swarm.py", - "line": 3599, - "signature": "(self, filename: str, swarmname: str, index: int, swarmVars: Optional[list] = None, outputPath: Optional[str] = '', time: Optional[int] = None, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential: Optional[bool] = False)", + "line": 1056, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "release if defined", + "REINIT policy: a swarm proxy is re-projected from the", + "*particles* on next access, so a mesh adapt should NOT", + "interpolate the old proxy values onto the new node", + "layout \u2014 that would freeze stale per-particle data on the" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": false + }, + { + "name": "__getitem__", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2334, + "signature": "(self, index)", "parameters": [ - { - "name": "filename", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "swarmname", - "type_hint": "str", - "default": null, - "description": "" - }, { "name": "index", - "type_hint": "int", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "swarmVars", - "type_hint": "Optional[list]", - "default": "None", - "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" - }, - { - "name": "time", - "type_hint": "Optional[int]", - "default": "None", - "description": "" - }, - { - "name": "compression", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "compressionType", - "type_hint": "Optional[str]", - "default": "'gzip'", - "description": "" - }, - { - "name": "force_sequential", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Save data to h5 and a corresponding xdmf for visualisation using h5py.\n\nParameters\n----------\nswarmName :\n Name of the swarm to save.\nswarmVars :\n List of swarm objects to save.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.\ntime :\n Attach the time to the generated xdmf.\ncompression :\n Whether to compress the h5 files [bool].\ncompressionType :\n The type of compression to use. 'gzip' and 'lzf' are the supported types, with 'gzip' as the default.", - "harvested_comments": [ - "This will eliminate the issue of whether or not to put path separators in the", - "outputPath. Also does the right thing if outputPath is \"\"", - "check the directory where we will write checkpoint", - "get directory", - "check if path exists" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", - "is_public": true + "parent_class": "IndexSwarmVariable", + "is_public": false }, { - "name": "vars", + "name": "_particle_coordinates", "kind": "property", "file": "src/underworld3/swarm.py", - "line": 3749, + "line": 3289, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "List of SwarmVariables attached to this swarm.\n\nReturns\n-------\nlist\n List of :class:`SwarmVariable` objects defined on this swarm.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], "parent_class": "Swarm", - "is_public": true + "is_public": false }, { - "name": "access", + "name": "_data_layout", "kind": "method", "file": "src/underworld3/swarm.py", - "line": 3900, - "signature": "(self, *writeable_vars)", + "line": 4790, + "signature": "(self, i, j = None)", "parameters": [ { - "name": "*writeable_vars", - "type_hint": "SwarmVariable", + "name": "i", + "type_hint": null, "default": null, "description": "" + }, + { + "name": "j", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Dummy access manager that provides deferred sync for backward compatibility.\nUses NDArray_With_Callback.delay_callbacks_global() internally.\n\nThis is a compatibility wrapper that allows existing code using the access()\ncontext manager to work with the new direct-access variable interfaces.\nAll variable modifications are deferred and synchronized at context exit.\n\nParameters\n----------\nwriteable_vars\n Variables that will be modified (ignored - all variables are writable\n with the new interface, this parameter is kept for API compatibility)\n\nReturns\n-------\nContext manager that defers variable synchronization until exit\n\nNotes\n-----\nThis method is deprecated. New code should access variable.data or\nvariable.array directly without requiring an access context.", - "harvested_comments": [ - "Use NDArray_With_Callback global delay context for deferred sync", - "This triggers all accumulated callbacks from all variables" + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], "parent_class": "Swarm", - "is_public": true + "is_public": false }, { - "name": "estimate_dt", + "name": "_get_map", "kind": "method", "file": "src/underworld3/swarm.py", - "line": 4249, - "signature": "(self, V_fn)", + "line": 4819, + "signature": "(self, var)", "parameters": [ { - "name": "V_fn", + "name": "var", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Calculates an appropriate advective timestep for the given\nmesh and velocity configuration.", + "existing_docstring": null, "harvested_comments": [ - "we'll want to do this on an element by element basis", - "for more general mesh", - "first let's extract a max global velocity magnitude", - "If vel is unit-aware (UnitAwareArray), nondimensionalise it to get", - "consistent nondimensional values that match mesh._radii" + "generate tree if not avaiable", + "get or generate map", + "we can't use numpy arrays directly as keys in python dicts, so", + "we'll use `xxhash` to generate a hash of array.", + "this shouldn't be an issue performance wise but we should test to be" ], - "status": "minimal", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], "parent_class": "Swarm", - "is_public": true + "is_public": false }, { - "name": "mesh", - "kind": "property", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 232, + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 662, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "The mesh associated with this swarm.\n\nReturns\n-------\nMesh\n The computational mesh containing the particles.", - "harvested_comments": [], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [ + "Display the primary variable", + "Display the history variable using the different symbol." + ], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "Symbolic", + "is_public": false }, { - "name": "data", - "kind": "property", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 243, + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 1018, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Eulerian", + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2035, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Particle coordinate data array.\n\nProvides direct read/write access to particle positions.\n\nReturns\n-------\nnumpy.ndarray\n Coordinate array of shape ``(n_particles, dim)``.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "SemiLagrangian", + "is_public": false }, { - "name": "particle_coordinates", - "kind": "property", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 256, + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3122, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance", + "Note: dt_physical is not tracked on the Lagrangian DDt classes,", + "so the viewer reports the expression and history depth only." + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian", + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 3479, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "# feedback on this instance", + "Note: dt_physical is not tracked on the Lagrangian DDt classes,", + "so the viewer reports the expression and history depth only." + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "Lagrangian_Swarm", + "is_public": false + }, + { + "name": "_append_petsc_path", + "kind": "function", + "file": "src/underworld3/utilities/__init__.py", + "line": 44, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [ + "get/import petsc_gen_xdmf from the original petsc installation", + "Handle Sphinx autodoc mocking" + ], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_increment", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 14, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "SwarmVariable holding particle coordinates.\n\nReturns\n-------\nSwarmVariable\n The internal coordinate variable (use ``.data`` for array access).", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "Stateful", + "is_public": false }, { - "name": "particle_cellid", - "kind": "property", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 267, + "name": "_get_state", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 17, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "SwarmVariable holding particle cell IDs.\n\nEach particle is associated with a mesh cell for efficient\nspatial operations.\n\nReturns\n-------\nSwarmVariable\n Cell ID variable (use ``.data`` for array access).", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "Stateful", + "is_public": false }, { - "name": "populate_petsc", + "name": "__get__", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 281, - "signature": "(self, fill_param: Optional[int] = 3, layout: Optional[SwarmPICLayout] = None)", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 26, + "signature": "(self, instance, owner)", "parameters": [ { - "name": "fill_param", - "type_hint": "Optional[int]", - "default": "3", + "name": "instance", + "type_hint": null, + "default": null, "description": "" }, { - "name": "layout", - "type_hint": "Optional[SwarmPICLayout]", - "default": "None", + "name": "owner", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Populate the swarm with particles throughout the domain.\n\nWhen using SwarmPICLayout.REGULAR, `fill_param` defines the number of points in each spatial direction.\nWhen using SwarmPICLayout.GAUSS, `fill_param` defines the number of quadrature points in each spatial direction.\nWhen using SwarmPICLayout.SUBDIVISION, `fill_param` defines the number times the reference cell is sub-divided.\n\nParameters\n----------\nfill_param:\n Parameter determining the particle count per cell for the given layout.\nlayout:\n Type of layout to use. Defaults to `SwarmPICLayout.REGULAR` for mesh objects with simplex\n type cells, and `SwarmPICLayout.GAUSS` otherwise.", - "harvested_comments": [ - "# Commenting this out for now.", - "# Code seems to operate fine without it, and the", - "# existing values are wrong. It should be something like", - "# `(elend-elstart)*fill_param^dim` for quads, and around", - "# half that for simplices, depending on layout." + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "partial", + "parent_class": "class_or_instance_method", + "is_public": false + }, + { + "name": "__str__", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 469, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "uw_object", + "is_public": false }, { - "name": "populate", + "name": "_ipython_display_", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 340, - "signature": "(self, fill_param: Optional[int] = 1)", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 479, + "signature": "(self_or_cls)", "parameters": [ { - "name": "fill_param", - "type_hint": "Optional[int]", - "default": "1", + "name": "self_or_cls", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Populate the swarm with particles throughout the domain.\n\nParameters\n----------\nfill_param:\n Parameter determining the particle count per cell (per dimension)\n for the given layout, using the mesh degree.\n\ncell_search:\n Use k-d tree to locate nearest cells (fails if this swarm is used to build a k-d tree)", + "existing_docstring": null, "harvested_comments": [ - "valid = newp_cells0 != -1", - "newp_coords = newp_coords0[valid]", - "newp_cells = newp_cells0[valid]", - "# Now make a series of copies to allow the swarm cycling to", - "# work correctly (if required)" + "# Docstring (static / class documentation)" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "uw_object", + "is_public": false }, { - "name": "add_particles_with_coordinates", + "name": "_object_viewer", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 436, - "signature": "(self, coordinatesArray) -> int", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 546, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "uw_object", + "is_public": false + }, + { + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 131, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UW_Pause", + "is_public": false + }, + { + "name": "_so_path", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 52, + "signature": "(cache_dir: Path, source_hash: str) -> Path", "parameters": [ { - "name": "coordinatesArray", - "type_hint": null, + "name": "cache_dir", + "type_hint": "Path", + "default": null, + "description": "" + }, + { + "name": "source_hash", + "type_hint": "str", "default": null, "description": "" } ], - "returns": "int", - "existing_docstring": "Add particles to the swarm using particle coordinates provided\nusing a numpy array.\n\nNote that particles with coordinates NOT local to the current processor will\nbe rejected / ignored.\n\nEither include an array with all coordinates to all processors\nor an array with the local coordinates.\n\nParameters\n----------\ncoordinatesArray : numpy.ndarray\n The numpy array containing the coordinate of the new particles. Array is\n expected to take shape n*dim, where n is the number of new particles, and\n dim is the dimensionality of the swarm's supporting mesh.\n\nReturns\n--------\nnpoints: int\n The number of points added to the local section of the swarm.", - "harvested_comments": [ - "### petsc appears to ignore columns that are greater than the mesh dim, but still worth including", - "-1 means no particles have been added yet", - "Here we update the swarm cycle values as required", - "self._Xorig.data[...] = coordinatesArray" + "returns": "Path", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "save", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 511, - "signature": "(self, filename: int, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential = False)", + "name": "_manifest_path", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 56, + "signature": "(cache_dir: Path, source_hash: str) -> Path", "parameters": [ { - "name": "filename", - "type_hint": "int", + "name": "cache_dir", + "type_hint": "Path", "default": null, "description": "" }, { - "name": "compression", - "type_hint": "Optional[bool]", - "default": "False", + "name": "source_hash", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "Path", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_lock_path", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 60, + "signature": "(cache_dir: Path, source_hash: str) -> Path", + "parameters": [ + { + "name": "cache_dir", + "type_hint": "Path", + "default": null, "description": "" }, { - "name": "compressionType", - "type_hint": "Optional[str]", - "default": "'gzip'", + "name": "source_hash", + "type_hint": "str", + "default": null, + "description": "" + } + ], + "returns": "Path", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "__new__", + "kind": "method", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 311, + "signature": "(cls, index, name = None)", + "parameters": [ + { + "name": "index", + "type_hint": null, + "default": null, "description": "" }, { - "name": "force_sequential", + "name": "name", "type_hint": null, - "default": "False", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Save the swarm coordinates to a h5 file.\n\nParameters\n----------\nfilename :\n The filename of the swarm checkpoint file to save to disk.\ncompression :\n Add compression to the h5 files (saves space but increases write times with increasing no. of processors)\ncompressionType :\n Type of compression to use, 'gzip' and 'lzf' supported. 'gzip' is default. Compression also needs to be set to 'True'.", - "harvested_comments": [ - "It seems to be a bad idea to mix mpi barriers with the access", - "context manager so the copy-free version of this seems to hang", - "when there are many active cores. This is probably why the parallel", - "h5py write hangs", - "It seems to be a bad idea to mix mpi barriers with the access" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "_JITConstant", + "is_public": false }, { - "name": "read_timestep", + "name": "_ccode", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 610, - "signature": "(self, base_filename: str, swarm_id: str, index: int, outputPath: Optional[str] = '')", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 319, + "signature": "(self, printer)", "parameters": [ { - "name": "base_filename", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "swarm_id", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "index", - "type_hint": "int", + "name": "printer", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" } ], "returns": null, - "existing_docstring": "Load particle coordinates from a saved timestep file.\n\nReads an HDF5 file containing particle coordinates and adds\nthem to the swarm using :meth:`add_particles_with_coordinates`.\n\nParameters\n----------\nbase_filename : str\n Base name for the output files.\nswarm_id : str\n Identifier for this swarm in the output.\nindex : int\n Timestep index to read.\noutputPath : str, optional\n Directory containing the output files.", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "_JITConstant", + "is_public": false + }, + { + "name": "__post_init__", + "kind": "method", + "file": "src/underworld3/utilities/_params.py", + "line": 88, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, "harvested_comments": [ - "## open up file with coords on all procs", - "### utilises the UW function for adding a swarm by an array" + "Auto-detect type from value if not specified (Python 3.10+ match/case)", + "Derive expected dimensionality from units (for validation)" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "Param", + "is_public": false }, { - "name": "add_variable", + "name": "__enter__", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 646, - "signature": "(self, name, size = 1, dtype = float, proxy_degree = 2, _nn_proxy = False)", + "file": "src/underworld3/utilities/_utils.py", + "line": 150, + "signature": "(self, *args, **kwargs)", "parameters": [ { - "name": "name", + "name": "*args", "type_hint": null, "default": null, "description": "" }, { - "name": "size", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "dtype", - "type_hint": null, - "default": "float", - "description": "" - }, - { - "name": "proxy_degree", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "_nn_proxy", + "name": "**kwargs", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create a new SwarmVariable attached to this swarm.\n\nParameters\n----------\nname : str\n Name for the variable.\nsize : int, optional\n Number of components per particle (default: 1 for scalar).\ndtype : type, optional\n Data type for the variable (default: float).\nproxy_degree : int, optional\n Polynomial degree for mesh proxy variable (default: 2).\n_nn_proxy : bool, optional\n Use nearest-neighbor proxy (internal use).\n\nReturns\n-------\nSwarmVariable\n New variable attached to this swarm.\n\nExamples\n--------\n>>> material = swarm.add_variable(\"material\", size=1, dtype=int)\n>>> temperature = swarm.add_variable(\"T\", size=1)", + "existing_docstring": null, "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "PICSwarm", - "is_public": true + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "CaptureStdout", + "is_public": false }, { - "name": "petsc_save_checkpoint", + "name": "__exit__", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 689, - "signature": "(self, swarmName: str, index: int, outputPath: Optional[str] = '')", + "file": "src/underworld3/utilities/_utils.py", + "line": 155, + "signature": "(self, *args, **kwargs)", "parameters": [ { - "name": "swarmName", - "type_hint": "str", + "name": "*args", + "type_hint": null, "default": null, "description": "" }, { - "name": "index", - "type_hint": "int", + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" } ], "returns": null, - "existing_docstring": "Use PETSc to save the swarm and attached data to a .pbin and xdmf file.\n\nParameters\n----------\nswarmName :\n Name of the swarm to save.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "CaptureStdout", + "is_public": false }, { - "name": "write_timestep", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 713, - "signature": "(self, filename: str, swarmname: str, index: int, swarmVars: Optional[list] = None, outputPath: Optional[str] = '', time: Optional[int] = None, compression: Optional[bool] = False, compressionType: Optional[str] = 'gzip', force_sequential: Optional[bool] = False)", + "name": "_key", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 27, + "signature": "(c, dim)", "parameters": [ { - "name": "filename", - "type_hint": "str", + "name": "c", + "type_hint": null, "default": null, "description": "" }, { - "name": "swarmname", - "type_hint": "str", + "name": "dim", + "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_to_petsc_aij", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 175, + "signature": "(csr)", + "parameters": [ { - "name": "index", - "type_hint": "int", + "name": "csr", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "swarmVars", - "type_hint": "Optional[list]", - "default": "None", - "description": "" - }, - { - "name": "outputPath", - "type_hint": "Optional[str]", - "default": "''", - "description": "" - }, - { - "name": "time", - "type_hint": "Optional[int]", - "default": "None", - "description": "" - }, - { - "name": "compression", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "compressionType", - "type_hint": "Optional[str]", - "default": "'gzip'", - "description": "" - }, - { - "name": "force_sequential", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Save data to h5 and a corresponding xdmf for visualisation using h5py.\n\nParameters\n----------\nswarmName :\n Name of the swarm to save.\nswarmVars :\n List of swarm objects to save.\nindex :\n An index which might correspond to the timestep or output number (for example).\noutputPath :\n Path to save the data. If left empty it will save the data in the current working directory.\ntime :\n Attach the time to the generated xdmf.\ncompression :\n Whether to compress the h5 files [bool].\ncompressionType :\n The type of compression to use. 'gzip' and 'lzf' are the supported types, with 'gzip' as the default.", + "existing_docstring": null, "harvested_comments": [ - "This will eliminate the issue of whether or not to put path separators in the", - "outputPath. Also does the right thing if outputPath is \"\"", - "check the directory where we will write checkpoint", - "get directory", - "check if path exists" + "Match the PETSc build's integer width for the CSR index arrays: a hard int32 cast", + "can overflow / mis-address entries on a 64-bit PetscInt build with large meshes." ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "vars", - "kind": "property", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 863, + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 223, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Dictionary of SwarmVariables attached to this swarm.\n\nReturns\n-------\ndict\n Mapping from variable names to :class:`SwarmVariable` objects.", + "existing_docstring": null, "harvested_comments": [], - "status": "partial", + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "NonDimensionalView", + "is_public": false }, { - "name": "access", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 873, - "signature": "(self, *writeable_vars)", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 975, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": false + }, + { + "name": "_kdtree_counts", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 117, + "signature": "() -> dict[str, int]", + "parameters": [], + "returns": "dict[str, int]", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", + "needs": [ + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_velocity_field_id", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 61, + "signature": "(solver)", "parameters": [ { - "name": "*writeable_vars", - "type_hint": "SwarmVariable", + "name": "solver", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "This context manager makes the underlying swarm variables data available to\nthe user. The data should be accessed via the variables `data` handle.\n\nAs default, all data is read-only. To enable writeable data, the user should\nspecify which variable they wish to modify.\n\nAt the conclusion of the users context managed block, numerous further operations\nwill be automatically executed. This includes swarm parallel migration routines\nwhere the swarm's `particle_coordinates` variable has been modified. The swarm\nvariable proxy mesh variables will also be updated for modifed swarm variables.\n\nParameters\n----------\nwriteable_vars\n The variables for which data write access is required.\n\nExample\n-------\n\n>>> import underworld3 as uw\n>>> someMesh = uw.discretisation.FeMesh_Cartesian()\n>>> with someMesh._deform_mesh():\n... someMesh.data[0] = [0.1,0.1]\n>>> someMesh.data[0]\narray([ 0.1, 0.1])", + "existing_docstring": null, "harvested_comments": [ - "if already accessed within higher level context manager, continue.", - "set flag so variable status can be known elsewhere", - "add to de-access list to rewind this later", - "grab numpy object, setting read only if necessary", - "increment variable state" + "velocity is field 0 in the Stokes saddle" ], - "status": "partial", + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "advection", + "name": "__new__", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1069, - "signature": "(self, V_fn, delta_t, order = 2, corrector = False, restore_points_to_domain_func = None, evalf = False, step_limit = True)", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 98, + "signature": "(cls, name = 't', **kwargs)", "parameters": [ { - "name": "V_fn", + "name": "name", "type_hint": null, - "default": null, + "default": "'t'", "description": "" }, { - "name": "delta_t", + "name": "**kwargs", "type_hint": null, "default": null, "description": "" - }, - { - "name": "order", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "corrector", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "restore_points_to_domain_func", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "step_limit", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, - "existing_docstring": "Advect particles using a velocity field.\n\nMoves particles according to the velocity field using forward\nEuler integration with automatic substepping based on CFL\nconditions. Handles particle migration between MPI ranks.\n\nParameters\n----------\nV_fn : sympy.Matrix or MeshVariable\n Velocity field (vector expression).\ndelta_t : float\n Total time to advect.\norder : int, optional\n Integration order (default: 2, not currently used).\ncorrector : bool, optional\n Apply predictor-corrector scheme (default: False).\nrestore_points_to_domain_func : callable, optional\n Function to restore particles that leave the domain.\nevalf : bool, optional\n Use numerical evaluation (True) or symbolic (False).\nstep_limit : bool, optional\n Apply CFL-based substepping limit (default: True).", - "harvested_comments": [ - "X0 holds the particle location at the start of advection", - "This is needed because the particles may be migrated off-proc", - "during timestepping.", - "Use current velocity to estimate where the particles would have", - "landed in an implicit step. WE CANT DO THIS WITH SUB-STEPPING unless" - ], - "status": "partial", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ - "NEEDS_RETURNS" + "NEEDS_OVERVIEW", + "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "TimeSymbol", + "is_public": false }, { - "name": "estimate_dt", + "name": "_ccode", "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1334, - "signature": "(self, V_fn)", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 113, + "signature": "(self, printer)", "parameters": [ { - "name": "V_fn", + "name": "printer", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Calculates an appropriate advective timestep for the given\nmesh and velocity configuration.", - "harvested_comments": [ - "we'll want to do this on an element by element basis", - "for more general mesh", - "first let's extract a max global velocity magnitude", - "If vel is unit-aware (UnitAwareArray), nondimensionalise it to get", - "consistent nondimensional values that match mesh._radii" - ], - "status": "minimal", + "existing_docstring": null, + "harvested_comments": [], + "status": "none", "needs": [ + "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "PICSwarm", - "is_public": true + "parent_class": "TimeSymbol", + "is_public": false }, { - "name": "advection", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1476, - "signature": "(self, V_fn, delta_t, order = 2, corrector = False, restore_points_to_domain_func = None, evalf = False, step_limit = True)", + "name": "_parse_thread_env", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 94, + "signature": "(name: str)", "parameters": [ { - "name": "V_fn", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "delta_t", - "type_hint": null, + "name": "name", + "type_hint": "str", "default": null, "description": "" - }, - { - "name": "order", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "corrector", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "restore_points_to_domain_func", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "evalf", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "step_limit", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, - "existing_docstring": "Advect nodal point particles and track back to mesh nodes.\n\nExtends parent advection with node-tracking logic. After\nadvection, particles are mapped back to their original mesh\nnodes for semi-Lagrangian interpolation.\n\nParameters\n----------\nV_fn : sympy.Matrix or MeshVariable\n Velocity field (vector expression).\ndelta_t : float\n Total time to advect.\norder : int, optional\n Integration order (default: 2).\ncorrector : bool, optional\n Apply predictor-corrector scheme (default: False).\nrestore_points_to_domain_func : callable, optional\n Function to restore particles that leave the domain.\nevalf : bool, optional\n Use numerical evaluation (True) or symbolic (False).\nstep_limit : bool, optional\n Apply CFL-based substepping limit (default: True).", + "existing_docstring": "Return integer thread count from environment variable, or None.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "NodalPointPICSwarm", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "psi_fn", - "kind": "property", - "file": "src/underworld3/systems/ddt.py", - "line": 104, - "signature": "(self)", + "name": "_apply_mpi_thread_policy", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 105, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "Current symbolic expression :math:`\\psi` being tracked.", + "existing_docstring": "Default thread policy for MPI runs.\n\nCaps common thread pools to 1 unless explicitly set by the user. This\nprevents MPI+BLAS oversubscription (large performance regressions).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "psi_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 109, - "signature": "(self, new_fn)", - "parameters": [ - { - "name": "new_fn", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "_units_view", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 275, + "signature": "()", + "parameters": [], "returns": null, - "existing_docstring": "Set the tracked symbolic expression.", + "existing_docstring": "Display units registry information following the established view() pattern.", "harvested_comments": [ - "Optionally, one could check for matching shape; here we update both." + "# Units Registry", + "## Common Units Examples:", + "Create quantities", + "Set model reference quantities", + "Fallback for non-Jupyter environments" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_history_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 130, - "signature": "(self)", + "name": "_apply_scaling", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 545, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "Copy current :math:`\\psi` to the first history slot ``psi_star[0]``.", - "harvested_comments": [ - "Update the first history element with a copy of the current \u03c8." - ], + "existing_docstring": "Internal context manager - DEPRECATED, use use_nondimensional_scaling() instead.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "initiate_history_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 135, - "signature": "(self)", + "name": "_is_scaling_active", + "kind": "function", + "file": "src/underworld3/__init__.py", + "line": 556, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.", - "harvested_comments": [ - "Propagate the initial history to all history steps." - ], + "existing_docstring": "Internal check - DEPRECATED, use is_nondimensional_scaling_active() instead.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 143, - "signature": "(self, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_dm_unstack_bcs", + "kind": "function", + "file": "src/underworld3/adaptivity.py", + "line": 475, + "signature": "(dm, boundaries, stacked_bc_label_name)", "parameters": [ { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundaries", + "type_hint": null, + "default": null, "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "stacked_bc_label_name", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Update history (alias for ``update_pre_solve``).", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Unpack boundary labels to the list of names.\n\nBUGFIX(#162): the auxiliary labels ``All_Boundaries`` (1001) and\n``Null_Boundary`` (666) are added to the ``boundaries`` enum by the\nMesh constructor (extend_enum) and populated outside this function\n\u2014 ``All_Boundaries`` by ``markBoundaryFaces`` in ``_from_gmsh`` /\n``_from_plexh5``, ``Null_Boundary`` by the Mesh constructor itself\nfrom the depth-0 stratum. The previous version of this function\niterated *every* boundary in the enum and removed its label, then\nonly repopulated those whose values appeared in the source stack\n(``Face Sets`` etc.). Since the auxiliary values 666/1001 are\nUW3-internal and never appear in gmsh-derived stacks, both labels\nwere silently destroyed.\n\nThe consequence (issue #162): ``BoxInternalBoundary``'s natural BCs\ncontributed nothing to the residual on any boundary, because PETSc's\n``DM_BC_NATURAL_FIELD`` integration relies on these consolidated\nlabels and the closure they imply. The same code path is used in\n``mesh_adapt_meshVar`` \u2014 adapted meshes also lost these labels.\n\nFix: after the standard remove/recreate cycle, restore the\nauxiliary labels using the same primitives the Mesh constructor /\n``_from_*`` helpers use.", + "harvested_comments": [ + "162): the auxiliary labels ``All_Boundaries`` (1001) and", + "162): ``BoxInternalBoundary``'s natural BCs", + "Clear labels just in case", + "ValueError if mismatch", + "BUGFIX(#162): expand the boundary label to include its closure" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_pre_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 152, - "signature": "(self, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_collect_metadata", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 69, + "signature": "(model) -> dict", "parameters": [ { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "model", + "type_hint": null, + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Pre-solve update hook (no-op for Symbolic).", + "returns": "dict", + "existing_docstring": "Build the metadata dict that gets written into ``/metadata`` attrs.\n\nStable, h5-friendly types only: strings, ints, floats, lists of\nstrings (stored as JSON for compactness and to keep h5 attrs\nscalar-typed where possible). No pickling, no repr of UW3 objects.", "harvested_comments": [ - "Default: no action." + "Mesh-derived info (gracefully absent if no mesh registered).", + "Swarm names (WeakValueDictionary, snapshot it).", + "State-bearer class summary (just the class names \u2014 useful in", + "h5ls without needing UW3 to interpret).", + "Tracker conventions (the model-dwelling record)." ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_post_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 161, - "signature": "(self, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_write_metadata_attrs", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 156, + "signature": "(h5group, metadata: dict) -> None", "parameters": [ { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", + "name": "h5group", + "type_hint": null, + "default": null, "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "metadata", + "type_hint": "dict", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Shift history chain after solve: :math:`\\psi^{*n} \\leftarrow \\psi^{*(n-1)}`.", - "harvested_comments": [ - "Shift history: copy each element down the chain." - ], + "returns": "None", + "existing_docstring": "Write a metadata dict as HDF5 attrs on a group. Plain types only.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "bdf", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 176, - "signature": "(self, order: Optional[int] = None)", + "name": "_bulk_dir_for", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 259, + "signature": "(wrapper_path: str) -> str", "parameters": [ { - "name": "order", - "type_hint": "Optional[int]", - "default": "None", + "name": "wrapper_path", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Compute the backward differentiation approximation of the time-derivative of \u03c8.\nFor order 1: bdf \u2261 \u03c8 - psi_star[0]", + "returns": "str", + "existing_docstring": "Convention: wrapper at `run.snap.h5` \u21d2 bulk at `run.snap.bulk/`.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Symbolic", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "adams_moulton_flux", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 199, - "signature": "(self, order: Optional[int] = None)", + "name": "_sanitise", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 265, + "signature": "(name: str) -> str", "parameters": [ { - "name": "order", - "type_hint": "Optional[int]", - "default": "None", + "name": "name", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nParameters\n----------\norder : int, optional\n Order of the approximation (1-3). Defaults to ``self.order``.\n\nReturns\n-------\nsympy.Matrix\n Weighted average of :math:`\\psi` and history terms.\n\nNotes\n-----\nThe Adams-Moulton formulas for order 1-3 are:\n\n- Order 1: :math:`\\theta \\psi + (1-\\theta) \\psi^*`\n- Order 2: :math:`\\frac{5\\psi + 8\\psi^* - \\psi^{**}}{12}`\n- Order 3: :math:`\\frac{9\\psi + 19\\psi^* - 5\\psi^{**} + \\psi^{***}}{24}`", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "Symbolic", - "is_public": true - }, - { - "name": "psi_fn", - "kind": "property", - "file": "src/underworld3/systems/ddt.py", - "line": 324, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Current symbolic expression :math:`\\psi` being tracked.", + "returns": "str", + "existing_docstring": "Sanitise a mesh / variable name for use as a filename component.\n\nReplaces anything that isn't alphanumeric or in ``._-`` with ``_``.\nFalls back to ``unnamed`` if the result is empty. The original name\nis preserved in HDF5 group attrs as the ``@name`` field.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Eulerian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "psi_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 329, - "signature": "(self, new_fn)", + "name": "_swarm_safe_name", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 569, + "signature": "(swarm) -> str", "parameters": [ { - "name": "new_fn", + "name": "swarm", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Set the tracked expression.", - "harvested_comments": [ - "self._psi_star_projection_solver.uw_function = self.psi_fn" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Eulerian", - "is_public": true - }, - { - "name": "update_history_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 379, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Copy current :math:`\\psi` to ``psi_star[0]`` via evaluation or projection.", - "harvested_comments": [ - "## update first value in history chain", - "## avoids projecting if function can be evaluated", - "print('copying data', flush=True)", - "if self.evalf:", - "self.psi_star[0].data[...] = uw.function.evalf(" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Eulerian", - "is_public": true - }, - { - "name": "initiate_history_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 408, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Initialize all history slots to the current value of :math:`\\psi`.", - "harvested_comments": [ - "## set up all history terms to the initial values" - ], + "returns": "str", + "existing_docstring": "Stable name for a swarm in the snapshot layout. Mirrors the\nin-memory snapshot's `_snapshot_stable_name`.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Eulerian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 419, - "signature": "(self, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_swarm_sidecar_filename", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 576, + "signature": "(swarm_safe: str, rank: int, size: int) -> str", "parameters": [ { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", + "name": "swarm_safe", + "type_hint": "str", + "default": null, "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "rank", + "type_hint": "int", + "default": null, + "description": "" + }, + { + "name": "size", + "type_hint": "int", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Update history (alias for ``update_pre_solve``).", + "returns": "str", + "existing_docstring": "Per-rank sidecar (phase 6 \u2014 parallel-safe).\n\nEach rank writes its own swarm sidecar; restoring requires the\nsame rank count. The filename carries both the writer's rank and\nthe total rank count so each file is self-describing.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Eulerian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_pre_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 428, - "signature": "(self, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_swarm_sidecar_pattern", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 586, + "signature": "(swarm_safe: str) -> str", "parameters": [ { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "swarm_safe", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Pre-solve update hook (no-op for Eulerian).", + "returns": "str", + "existing_docstring": "Pattern stored in the wrapper for readers to fill in their own\n(rank, size) when locating their sidecar.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Eulerian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_post_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 436, - "signature": "(self, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_write_swarm_to_sidecar", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 592, + "signature": "(swarm, sidecar_path: str) -> dict", "parameters": [ { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", + "name": "swarm", + "type_hint": null, + "default": null, "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "sidecar_path", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Shift history chain after solve: :math:`\\psi^{*n} \\leftarrow \\psi^{*(n-1)}`.", + "returns": "dict", + "existing_docstring": "Write a swarm's local-particle state to a sidecar h5 file.\n\nReturns a record dict the caller uses to populate the wrapper's\n/swarms/{name}/ group.", "harvested_comments": [ - "if average_over_dt:", - "phi = min(1.0, dt / self.dt_physical)", - "## copy values down the chain", - "## update the history fn", - "### update the first value in the chain" + "File-level metadata \u2014 h5ls -v on the sidecar tells you what", + "it is without needing UW3 (matches the wrapper's bar).", + "Parallel-write provenance \u2014 each per-rank sidecar carries", + "its writer's identity so the reader can sanity-check it", + "opened the right file." ], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Eulerian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "bdf", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 474, - "signature": "(self, order = None)", + "name": "_read_swarm_from_sidecar", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 645, + "signature": "(swarm, sidecar_path: str) -> None", "parameters": [ { - "name": "order", + "name": "swarm", "type_hint": null, - "default": "None", + "default": null, + "description": "" + }, + { + "name": "sidecar_path", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Backwards differentiation form for calculating DuDt\nNote that you will need ``bdf`` / :math:`\\delta t` in computing derivatives", - "harvested_comments": [], - "status": "minimal", + "returns": "None", + "existing_docstring": "Restore swarm state from a sidecar file. Mirrors\n:meth:`Swarm.apply_snapshot_payload` exactly \u2014 clear local\nparticles, re-add at saved coords, write var data back.", + "harvested_comments": [ + "Clear local population. removePoint is O(1) per call (last point),", + "so this is O(N) total \u2014 same approach as Swarm.apply_snapshot_payload.", + "Invalidate canonical-data caches so subsequent var.data reads", + "re-resolve to the rebuilt PETSc fields.", + "Restore counted as a population change (matches in-memory path)." + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Eulerian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "adams_moulton_flux", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 500, - "signature": "(self, order = None)", + "name": "_serialise_field", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 754, + "signature": "(h5group, name: str, value: Any) -> None", "parameters": [ { - "name": "order", + "name": "h5group", "type_hint": null, - "default": "None", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": "Any", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nParameters\n----------\norder : int, optional\n Order of the approximation (1-3). Defaults to ``self.order``.\n\nReturns\n-------\nsympy.Basic\n Weighted average of :math:`\\psi` and history terms.", - "harvested_comments": [ - "am = self.theta*self.psi_fn + ((1.-self.theta)*self.psi_star[0].sym)" - ], - "status": "complete", - "needs": [], - "parent_class": "Eulerian", - "is_public": true - }, - { - "name": "psi_fn", - "kind": "property", - "file": "src/underworld3/systems/ddt.py", - "line": 690, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Current symbolic expression :math:`\\psi` being tracked.", + "returns": "None", + "existing_docstring": "Write a Python value into an HDF5 group as attr/dataset/subgroup.\n\nThe shape of the storage records the type:\n - attr scalar (int/float/bool/str) for scalars\n - attr `` = '__none__' for None\n - attr `__json` for JSON-encodable lists / nested simple structures\n - dataset `` for numpy arrays\n - subgroup `` for dict values, recursing\n - attr `__skipped` = '' for anything else", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SemiLagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "psi_fn", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 695, - "signature": "(self, new_fn)", + "name": "_group_to_dict", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 801, + "signature": "(h5group) -> dict", "parameters": [ { - "name": "new_fn", + "name": "h5group", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Set the tracked expression.", + "returns": "dict", + "existing_docstring": "Read a subgroup back as a plain dict \u2014 symmetric to the dict\nbranch of :func:`_serialise_field`. Recurses for nested groups.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SemiLagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 708, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False, dt_physical: Optional = None)", + "name": "_deserialise_field", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 829, + "signature": "(h5group, name: str, fallback: Any) -> Any", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "h5group", + "type_hint": null, "default": null, "description": "" }, { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "name", + "type_hint": "str", + "default": null, "description": "" }, { - "name": "dt_physical", - "type_hint": "Optional", - "default": "None", + "name": "fallback", + "type_hint": "Any", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Update history (alias for ``update_pre_solve``).", - "harvested_comments": [], - "status": "minimal", + "returns": "Any", + "existing_docstring": "Inverse of :func:`_serialise_field`. Returns ``fallback`` if the\nfield was skipped at write time, so we don't clobber a sensible\ndefault with a placeholder.", + "harvested_comments": [ + "h5py.Dataset", + "Skipped at write time \u2014 keep the current value rather than", + "clobber it with a placeholder." + ], + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SemiLagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_post_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 719, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False, dt_physical: Optional[float] = None)", + "name": "_write_state_bearer_to_group", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 860, + "signature": "(group, obj) -> None", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "group", + "type_hint": null, "default": null, "description": "" }, { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "dt_physical", - "type_hint": "Optional[float]", - "default": "None", + "name": "obj", + "type_hint": null, + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Post-solve update hook (no-op for SemiLagrangian).", + "returns": "None", + "existing_docstring": "Serialise a Snapshottable's .state into the given HDF5 group.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SemiLagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_pre_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 729, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False, dt_physical: Optional[float] = None)", + "name": "_read_state_bearer_into", + "kind": "function", + "file": "src/underworld3/checkpoint/disk_snapshot.py", + "line": 871, + "signature": "(group, obj) -> None", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "group", + "type_hint": null, "default": null, "description": "" }, { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "dt_physical", - "type_hint": "Optional[float]", - "default": "None", + "name": "obj", + "type_hint": null, + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Sample upstream values along characteristics before solve.", - "harvested_comments": [ - "# Progress from the oldest part of the history", - "1. Copy the stored values down the chain in preparation for the next timestep", - "The history term is the nodel value of psi_fn offset back along the characteristics", - "according to the timestep.", - "- psi_star[0] is the current value of psi_fn, sampled" - ], - "status": "minimal", + "returns": "None", + "existing_docstring": "Restore a Snapshottable's .state from a group written by\n:func:`_write_state_bearer_to_group`. Uses the live ``obj.state``\nas a type template \u2014 fields that were skipped at write time keep\ntheir current value rather than being clobbered by a placeholder.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SemiLagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "bdf", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1074, - "signature": "(self, order = None)", + "name": "_swarm_stable_name", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 74, + "signature": "(swarm) -> str", "parameters": [ { - "name": "order", + "name": "swarm", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Backwards differentiation form for calculating DuDt\nNote that you will need ``bdf`` / :math:`\\delta t` in computing derivatives", + "returns": "str", + "existing_docstring": "Per-process stable name for a swarm. Uses uw_object instance number.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SemiLagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "adams_moulton_flux", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1100, - "signature": "(self, order = None)", + "name": "_is_internal_swarmvar", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 158, + "signature": "(var_name: str) -> bool", "parameters": [ { - "name": "order", - "type_hint": null, - "default": "None", + "name": "var_name", + "type_hint": "str", + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nParameters\n----------\norder : int, optional\n Order of the approximation (0-3). Defaults to ``self.order``.\n\nReturns\n-------\nsympy.Basic\n Weighted average of :math:`\\psi` and history terms.", + "returns": "bool", + "existing_docstring": "Filter PETSc-managed internal swarm variables from user capture.\n\n``DMSwarmPIC_coor`` is captured separately via the particle-coords\npath. ``DMSwarm_X0`` and ``DMSwarm_remeshed`` carry recycle-related\nbookkeeping that is regenerated on next solve and is out of scope\nfor v1 capture.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "SemiLagrangian", - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false }, { - "name": "update", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1236, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_state_bearer_key", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 220, + "signature": "(obj) -> str", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "obj", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" } ], - "returns": null, - "existing_docstring": "Update history (alias for ``update_post_solve``).", + "returns": "str", + "existing_docstring": "Stable per-process key for a Snapshottable. ``instance_number``\ncomes from ``uw_object`` and is unique across the run.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_pre_solve", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1246, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "name": "_capture_state_bearer", + "kind": "function", + "file": "src/underworld3/checkpoint/snapshot.py", + "line": 226, + "signature": "(snap: Snapshot, obj) -> None", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "snap", + "type_hint": "Snapshot", "default": null, "description": "" }, { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "obj", + "type_hint": null, + "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Pre-solve update hook (no-op for Lagrangian).", + "returns": "None", + "existing_docstring": "Pull ``obj.state`` and store a deep copy.\n\nDeep copy ensures later mutations on the live state-bearer don't\nleak into the captured token. The dataclass itself is the storage\nhere (no separate backend.save_vector call) because state\ndataclasses are small Python objects, not bulk numerical arrays.\nv1.1's on-disk backend will route the dataclass through the\nbackend (HDF5 attrs/groups); v1 holds them in the in-memory Snapshot\ndirectly.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Lagrangian", - "is_public": true + "parent_class": null, + "is_public": false }, { - "name": "update_post_solve", + "name": "_convert_coords_to_tree_units", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1255, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "file": "src/underworld3/ckdtree.pyx", + "line": 127, + "signature": "def _convert_coords_to_tree_units(self, coords):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Convert query coordinates to match the KD-tree's coordinate system.\n\n Parameters\n ----------\n coords : array-like\n Query coordinates (may or may not have units)\n\n Returns\n -------\n np.ndarray\n Coordinates converted to tree's coordinate system (raw numpy array)\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_list_valid_parameters", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 98, + "signature": "(cls_type)", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "cls_type", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Shift history chain and advect swarm after solve.", - "harvested_comments": [ - "copy the information down the chain", - "Now update the swarm variable", - "Now update the swarm locations" - ], + "existing_docstring": "List valid parameter names for error messages.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian", - "is_public": true + "parent_class": "_ParameterBase", + "is_public": false }, { - "name": "bdf", + "name": "_q", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1292, - "signature": "(self, order = None)", + "file": "src/underworld3/constitutive_models.py", + "line": 533, + "signature": "(self, ddu)", "parameters": [ { - "name": "order", + "name": "ddu", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Backwards differentiation form for calculating DuDt\nNote that you will need ``bdf`` / :math:`\\delta t` in computing derivatives", + "existing_docstring": "Generic flux term", "harvested_comments": [ - "special case - no history term (catch )" + "tensor multiplication" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian", - "is_public": true + "parent_class": "Constitutive_Model", + "is_public": false }, { - "name": "adams_moulton_flux", + "name": "_reset", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1319, - "signature": "(self, order = None)", - "parameters": [ - { - "name": "order", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/constitutive_models.py", + "line": 596, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nParameters\n----------\norder : int, optional\n Order of the approximation (0-3). Defaults to ``self.order``.\n\nReturns\n-------\nsympy.Basic\n Weighted average of :math:`\\psi` and history terms.", + "existing_docstring": "Flags that the expressions in the consitutive tensor need to be refreshed and also that the\nsolver will need to rebuild the stiffness matrix and jacobians", "harvested_comments": [ - "Special case - no history term" + "Propagate invalidation to solver if we have a reference.", + "A constitutive parameter change affects the F1 pointwise function", + "only \u2014 the DM, fields, and BCs are unchanged. The solver's", + "_build() can swap functions in place without DM teardown." ], - "status": "complete", - "needs": [], - "parent_class": "Lagrangian", - "is_public": true + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Constitutive_Model", + "is_public": false }, { - "name": "update", + "name": "_update_history_coefficients", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1440, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", - "parameters": [ - { - "name": "dt", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - } - ], + "file": "src/underworld3/constitutive_models.py", + "line": 634, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Update history (alias for ``update_post_solve``).", + "existing_docstring": "Uniform pre-solve hook the Stokes solver calls before each solve.\n\nDefault: no-op (non-stress-history models have nothing to update).\nBDF-style stress-history subclasses (VEP, TI-VEP) override to\ndelegate to ``_update_bdf_coefficients``. ETD-2 / exponential\nsubclasses (e.g. ``MaxwellExponentialFlowModel``) override to\nupdate \u03b1, \u03c6 on the DDt. The solver dispatches uniformly through\nthis method \u2014 no ``isinstance`` checks at the solver layer.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Lagrangian_Swarm", - "is_public": true + "parent_class": "Constitutive_Model", + "is_public": false }, { - "name": "update_pre_solve", + "name": "_update_history_post_solve", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1450, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", - "parameters": [ - { - "name": "dt", - "type_hint": "float", - "default": null, - "description": "" - }, - { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - } + "file": "src/underworld3/constitutive_models.py", + "line": 646, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Uniform post-solve hook the Stokes solver calls after each solve.\n\nDefault: no-op. Subclasses that store extra integrator state for\nthe next step (e.g. ETD-2 storing \u03b5\u0307\u207f in ``forcing_star``) override.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "Constitutive_Model", + "is_public": false + }, + { + "name": "_build_c_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 663, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Pre-solve update hook (no-op for Lagrangian_Swarm).", + "existing_docstring": "Return the identity tensor of appropriate rank (e.g. for projections)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian_Swarm", - "is_public": true + "parent_class": "Constitutive_Model", + "is_public": false }, { - "name": "update_post_solve", + "name": "_q", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1459, - "signature": "(self, dt: float, evalf: Optional[bool] = False, verbose: Optional[bool] = False)", + "file": "src/underworld3/constitutive_models.py", + "line": 798, + "signature": "(self, edot)", "parameters": [ { - "name": "dt", - "type_hint": "float", + "name": "edot", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": "Shift history chain and evaluate current :math:`\\psi` on swarm.", + "existing_docstring": "Apply constitutive tensor to strain rate to compute stress.", "harvested_comments": [ - "copy the information down the chain", - "Now update the swarm variable", - "psi_star_0 = self.psi_star[0]", - "with self.swarm.access(psi_star_0):", - "for i in range(psi_star_0.shape[0]):" + "tensor multiplication" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian_Swarm", - "is_public": true + "parent_class": "ViscousFlowModel", + "is_public": false }, { - "name": "bdf", + "name": "_build_c_tensor", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1512, - "signature": "(self, order = None)", - "parameters": [ - { - "name": "order", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/constitutive_models.py", + "line": 842, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Backwards differentiation form for calculating DuDt\nNote that you will need ``bdf`` / :math:`\\delta t` in computing derivatives", + "existing_docstring": "For this constitutive law, we expect just a viscosity function", "harvested_comments": [ - "This is actually calculated over several steps so scaling is required" + "Check for tensor forms first (Mandel matrix or full rank-4 tensor)", + "Mandel form of constitutive tensor", + "Full rank-4 tensor", + "Scalar viscosity case", + "UWexpression has __getitem__ from MathematicalMixin, making it Iterable," ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian_Swarm", - "is_public": true + "parent_class": "ViscousFlowModel", + "is_public": false }, { - "name": "adams_moulton_flux", + "name": "_update_bdf_coefficients", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1541, - "signature": "(self, order = None)", - "parameters": [ - { - "name": "order", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": null, - "existing_docstring": "Adams-Moulton flux approximation for implicit time integration.\n\nParameters\n----------\norder : int, optional\n Order of the approximation (1-3). Defaults to ``self.order``.\n\nReturns\n-------\nsympy.Basic\n Weighted average of :math:`\\psi` and history terms.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "Lagrangian_Swarm", - "is_public": true - }, - { - "name": "start", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 74, - "signature": "()", + "file": "src/underworld3/constitutive_models.py", + "line": 1436, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Start PETSc performance logging.\n\nCall this at the beginning of your script/notebook to enable comprehensive\nperformance tracking. Works immediately in Jupyter - no environment variables needed!\n\nThis captures:\n- Decorated Python functions\n- All PETSc operations (MatMult, KSPSolve, VecNorm, etc.)\n- Memory usage and allocation\n- Floating point operations (flops)\n- MPI communication (in parallel runs)\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... do work ...\n>>> uw.timing.print_table() # View results\n\nNotes\n-----\n- Safe to call multiple times (subsequent calls are no-ops)\n- Zero overhead when not enabled\n- Can be called anywhere before performance-critical code", + "existing_docstring": "Update BDF coefficient UWexpressions from current dt_elastic and DDt history.\n\nCall this before each solve so that the constants[] array carries the\ncorrect coefficients to the compiled pointwise functions. The coefficient\nUWexpressions (_bdf_c0..c3) are referenced symbolically in ve_effective_viscosity,\nE_eff, and stress() \u2014 their numeric values flow through PetscDSSetConstants.\n\nWhen the timestep ratio exceeds ``_max_dt_ratio_for_higher_order``,\nBDF-2+ coefficients can cause negative stress extrapolation if the\nstress history is non-smooth (e.g. after a yield event). In this case\nwe fall back to BDF-1 coefficients for safety.", "harvested_comments": [ - "... do work ...", - "View results" + "Guard: fall back to BDF-1 when timestep increases too rapidly", + "symbolic dt \u2014 can't evaluate, keep requested order", + "Pad to length 4" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false }, { - "name": "stop", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 104, - "signature": "()", + "name": "_update_history_coefficients", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1479, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Stop PETSc logging (currently a no-op - PETSc logging runs until view()).\n\nProvided for API compatibility with legacy timing module.\nPETSc logging is lightweight and can run continuously.", + "existing_docstring": "Pre-solve hook: refresh integrator coefficients.\n\nDispatches on ``(self._integrator, self._order)``:\n- ``\"bdf\"`` (order 1 or 2): updates BDF c-coefficients via\n :py:meth:`_update_bdf_coefficients`.\n- ``\"etd\"`` order=2 (Phase B ETD-2): updates \u03b1, \u03c6 on the DDt\n from ``\u03c4_VE = \u03b7/\u03bc``; forcing-history slot active.\n- ``\"etd\"`` order=1 (ETD-1): updates \u03b1, \u03c6 as for ETD-2 then\n forces ``\u03c6 = \u03b1`` so the ``(\u03c6-\u03b1)\u00b7\u03b5\u0307*`` term zeros out \u2014 fully\n L-stable single-step, no forcing-history slot needed.", "harvested_comments": [ - "PETSc logging doesn't need explicit stopping" + "ETD-1 reduction: \u03c6 = \u03b1 makes the (\u03c6-\u03b1)\u00b7\u03b5\u0307* term zero", + "AND turns (1-\u03c6)\u00b7\u03b5\u0307 into (1-\u03b1)\u00b7\u03b5\u0307." ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false }, { - "name": "reset", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 114, - "signature": "()", + "name": "_update_history_post_solve", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1520, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Reset timing data.\n\nClears all accumulated PETSc logging data and starts fresh.\nUseful for timing specific sections of code.\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... setup code (not timed) ...\n>>> uw.timing.reset()\n>>> # ... performance-critical code (timed) ...\n>>> uw.timing.print_table()", - "harvested_comments": [ - "... setup code (not timed) ...", - "... performance-critical code (timed) ...", - "Note: PETSc doesn't have a true \"reset\" - we'd need to stop and restart", - "For now, this is a no-op but documented for API compatibility" + "existing_docstring": "Post-solve hook.\n\n- BDF / ETD-1: no-op (no forcing-history slot).\n- ETD-2: refresh ``forcing_star`` from the just-solved \u03b5\u0307 for\n the next step's history term.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false + }, + { + "name": "_unclipped_ve_viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 1637, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Unclipped viscoelastic viscosity (no yield wrap), depends on integrator.\n\n- BDF: ``ve_effective_viscosity = \u03b7\u00b7\u03bc\u0394t/(c\u2080\u00b7\u03b7 + \u03bc\u0394t)`` \u2014\n baked-in time-integration factor for backward differentiation.\n- ETD-2: ``\u03b7`` (raw) \u2014 the time-integration factor ``(1-\u03c6)`` is\n carried symbolically in :py:attr:`E_eff`, not folded into\n this viscosity.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false }, { - "name": "print_table", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 138, - "signature": "(filename = None, format = 'auto')", - "parameters": [ - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "format", - "type_hint": null, - "default": "'auto'", - "description": "" - } + "name": "_build_c_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 1774, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "For this constitutive law, we expect just a viscosity function", + "harvested_comments": [ + "inner_self = self.Parameters", + "CRITICAL: Use .sym property to avoid UWexpression array corruption issues", + "See ViscousFlowModel._build_c_tensor() for detailed explanation" ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ViscoElasticPlasticFlowModel", + "is_public": false + }, + { + "name": "_build_c_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2119, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Display comprehensive performance results.\n\nShows timing for:\n- Decorated Python functions\n- PETSc operations (solvers, matrix ops, etc.)\n- Memory usage\n- Flop counts\n- MPI communication (parallel runs)\n\nParameters\n----------\nfilename : str, optional\n If provided, write results to file. Extension determines format:\n - `.csv` : Spreadsheet-compatible CSV format\n - `.txt` or other : Human-readable ASCII table\nformat : str, optional\n Override automatic format detection:\n - \"auto\" : Detect from filename (default)\n - \"ascii\" : Human-readable table\n - \"csv\" : Comma-separated values\n\nExample\n-------\n>>> uw.timing.start()\n>>> # ... do work ...\n>>> uw.timing.print_table() # Print to console\n>>> uw.timing.print_table(\"results.csv\") # Save as CSV", + "existing_docstring": "Build isotropic diffusivity tensor from scalar.", "harvested_comments": [ - "... do work ...", - "Print to console", - "Save as CSV" + "Scalar diffusivity case", + "Use element-wise construction (consistent with ViscousFlowModel pattern)", + "to handle UWexpression properly and preserve for JIT unwrapping", + "Diagonal element: kappa", + "Wrap if bare UWexpression to avoid Iterable check failure" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "DiffusionModel", + "is_public": false }, { - "name": "enable_petsc_logging", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 176, - "signature": "()", + "name": "_build_c_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2211, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Enable PETSc performance logging.\n\nCalled automatically by start(). Can also be called directly.\nSafe to call multiple times.", + "existing_docstring": "Constructs the anisotropic (diagonal) tensor from the diffusivity vector.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "AnisotropicDiffusionModel", + "is_public": false + }, + { + "name": "_build_c_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2401, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "For this constitutive law, we expect just a permeability function", "harvested_comments": [ - "Already enabled" + "Scalar permeability case", + "Use element-wise construction (consistent with ViscousFlowModel and DiffusionModel)", + "to handle UWexpression properly and preserve for JIT unwrapping", + "Diagonal element: kappa", + "Wrap if bare UWexpression to avoid Iterable check failure" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "DarcyFlowModel", + "is_public": false + }, + { + "name": "_build_c_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2570, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "For this constitutive law, we expect two viscosity functions\nand a sympy row-matrix that describes the director components n_{i}", + "harvested_comments": [ + "Use .sym to get sympy expressions from Parameters", + "Use element-wise construction (same pattern as ViscousFlowModel).", + "UWexpression has __getitem__ from MathematicalMixin, making it appear", + "\"Iterable\" to SymPy's array multiplication operator, which rejects it.", + "Element-wise construction avoids this by creating Mul objects that" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TransverseIsotropicFlowModel", + "is_public": false + }, + { + "name": "_update_etd_coefficients", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2956, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Refresh DDt's (\u03b1, \u03c6) UWexpressions from \u03c4_eff = \u03b7_1/\u03bc.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false + }, + { + "name": "_update_history_coefficients", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 2980, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Pre-solve hook \u2014 dispatches BDF, ETD (order 1/2), or hybrid.\n\nBDF: refresh ``_bdf_c0..c3``. ETD-2 (order=2): refresh ``\u03b1, \u03c6``\non the DDt from ``\u03b7_1/\u03bc``. ETD-1 (order=1): same plus force\n``\u03c6 = \u03b1`` so the ``(\u03c6-\u03b1)\u00b7\u03b5\u0307*`` history term vanishes. Hybrid:\nrefresh both BDF and ETD-2 \u2014 flux uses both per-spatial weight.", + "harvested_comments": [ + "ETD-1 reduction: \u03c6 = \u03b1 zeros (\u03c6-\u03b1)\u00b7\u03b5\u0307* and turns", + "(1-\u03c6)\u00b7\u03b5\u0307 into (1-\u03b1)\u00b7\u03b5\u0307.", + "Hybrid uses BOTH integrators with spatial blend; update", + "both coefficient sets each step." ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "print_petsc_log", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 195, - "signature": "(filename = None, format = 'auto')", - "parameters": [ - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "format", - "type_hint": null, - "default": "'auto'", - "description": "" - } - ], + "name": "_update_history_post_solve", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3004, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Display or save PETSc performance logging summary.\n\nParameters\n----------\nfilename : str, optional\n If provided, write log to this file. Otherwise print to console.\n File extension determines format:\n - `.csv` : Comma-separated values (spreadsheet-compatible)\n - `.txt` or other : Human-readable ASCII table (default)\nformat : str, optional\n Override automatic format detection. Options:\n - \"auto\" : Detect from filename extension (default)\n - \"ascii\" : Human-readable table\n - \"csv\" : Comma-separated values\n\nExample\n-------\n>>> uw.timing.start()\n>>> # ... run simulation ...\n>>> uw.timing.print_petsc_log() # Console output\n>>> uw.timing.print_petsc_log(\"timing.csv\") # CSV for analysis", - "harvested_comments": [ - "... run simulation ...", - "Console output", - "CSV for analysis", - "Determine format from extension or explicit parameter", - "Create viewer with appropriate format" - ], - "status": "partial", + "existing_docstring": "Post-solve hook \u2014 refresh forcing_star for ETD-2 / hybrid.\n\nETD-1 (order=1) doesn't need this; the (\u03c6-\u03b1)\u00b7\u03b5\u0307* term zeros out.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "routine_timer_decorator", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 256, - "signature": "(routine, class_name = None)", - "parameters": [ - { - "name": "routine", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "class_name", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "name": "_update_bdf_coefficients", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3028, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Decorator that registers a function as a PETSc timing event.\n\nNo environment variables needed - works immediately!\n\nParameters\n----------\nroutine : callable\n Function or method to decorate\nclass_name : str, optional\n Class name for better event labeling (auto-detected for methods)\n\nReturns\n-------\ncallable\n Wrapped function that tracks calls via PETSc events\n\nExample\n-------\n>>> @uw.timing.routine_timer_decorator\n>>> def expensive_computation():\n>>> # ... complex calculations ...\n>>> return result\n>>>\n>>> uw.timing.start()\n>>> expensive_computation()\n>>> uw.timing.print_table() # Shows timing for expensive_computation\n\nNotes\n-----\n- First call registers the PETSc event (one-time cost)\n- Subsequent calls just increment counters (negligible overhead)\n- Events appear in PETSc log with full statistics", + "existing_docstring": "Update BDF coefficient UWexpressions with blending.", "harvested_comments": [ - "... complex calculations ...", - "Shows timing for expensive_computation", - "Create event name", - "Register PETSc event (happens once per function)", - "Begin/end tracking - PETSc handles all statistics!" + "BDF order blending \u2014 see ``self._bdf_blend`` docstring.", + "Linear mix of BDF-1 and the requested-order coefficients.", + "\u03b1=0 \u2192 pure BDF-1; \u03b1=1 \u2192 no blend (skip)." ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "add_timing_to_module", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 379, - "signature": "(mod)", + "name": "_e_eff_for", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3106, + "signature": "(self, integrator_mode)", "parameters": [ { - "name": "mod", + "name": "integrator_mode", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Automatically add timing decorators to all classes and functions in a module.\n\nRecursively walks through a module, decorating all classes and their methods.\nUseful for comprehensive profiling of entire subsystems.\n\nParameters\n----------\nmod : module\n Python module to decorate\n\nExample\n-------\n>>> import underworld3 as uw\n>>> import underworld3.systems\n>>> uw.timing.add_timing_to_module(uw.systems) # Time all solver classes\n>>> uw.timing.start()\n>>> # ... use solvers ...\n>>> uw.timing.print_table() # See detailed solver timing\n\nNotes\n-----\n- Only decorates classes/functions defined in the specified module\n- Skips built-in modules and external dependencies\n- Safe to call multiple times (avoids double-decoration)", + "existing_docstring": "Build E_eff for a given integrator mode (without storing on\n``self._E_eff``). Used by both the public :py:attr:`E_eff` and\nthe hybrid ``stress()`` path which needs both forms in one\nevaluation.", "harvested_comments": [ - "Time all solver classes", - "... use solvers ...", - "See detailed solver timing", - "Already decorated", - "Find submodules to recurse into" + "BDF default" ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "create_event", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 457, - "signature": "(name)", - "parameters": [ - { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "_plastic_effective_viscosity", + "kind": "property", + "file": "src/underworld3/constitutive_models.py", + "line": 3199, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Create a custom PETSc event for manual timing.\n\nUseful for timing specific code sections without decorators.\n\nParameters\n----------\nname : str\n Name for the event (appears in timing output)\n\nReturns\n-------\nPETSc.Log.Event\n Event object with begin() and end() methods\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>>\n>>> my_event = uw.timing.create_event(\"DataProcessing\")\n>>> my_event.begin()\n>>> # ... complex data processing ...\n>>> my_event.end()\n>>>\n>>> uw.timing.print_table() # Shows \"DataProcessing\" timing", + "existing_docstring": "Plastic viscosity from resolved fault-plane shear strain rate.\n\nComputes the in-plane shear magnitude using Pythagoras:\n T = \u03b5\u0307_eff \u00b7 n (traction-like vector on fault)\n \u03b5\u0307_n = T \u00b7 n (normal component)\n |\u03b3\u0307| = \u221a(|T|\u00b2 - \u03b5\u0307_n\u00b2) (in-plane shear magnitude)\n\nThis works in both 2D and 3D \u2014 no explicit tangent vector needed.\nThe formula 2\u03b7\u2081_pl = \u03c4_y / |\u03b3\u0307| is the same pattern as isotropic\nDrucker-Prager but projected onto the fault plane.", "harvested_comments": [ - "... complex data processing ...", - "Shows \"DataProcessing\" timing" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "get_summary", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 493, - "signature": "(filter_uw = True, min_time = 0.001, sort_by = 'time')", - "parameters": [ - { - "name": "filter_uw", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "min_time", - "type_hint": null, - "default": "0.001", - "description": "" - }, - { - "name": "sort_by", - "type_hint": null, - "default": "'time'", - "description": "" - } + "Resolve strain rate onto fault plane via Pythagoras", + "\"traction\" vector on fault", + "normal component", + "in-plane shear\u00b2" ], - "returns": null, - "existing_docstring": "Get user-friendly timing summary focusing on UW3 operations.\n\nFilters PETSc's comprehensive log to show only the most relevant timing\ninformation for UW3 users. By default, shows only UW3 operations (not\nlow-level PETSc internals).\n\nParameters\n----------\nfilter_uw : bool, optional\n If True (default), show only UW3 operations. If False, show all PETSc events.\nmin_time : float, optional\n Minimum time (seconds) for an event to be displayed. Default 0.001 (1ms).\n Helps filter out negligible operations.\nsort_by : str, optional\n Sort events by: 'time' (default), 'count', or 'name'.\n\nReturns\n-------\ndict\n Dictionary with keys:\n - 'events': List of (name, count, time, percent) tuples\n - 'total_time': Total execution time\n - 'num_events': Number of events displayed\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... do work ...\n>>>\n>>> # Get UW3-focused summary\n>>> summary = uw.timing.get_summary()\n>>> for name, count, time, pct in summary['events']:\n>>> print(f\"{name:40s} {count:5d} calls {time:8.3f}s ({pct:5.1f}%)\")\n>>>\n>>> # Get all events (including PETSc internals)\n>>> full_summary = uw.timing.get_summary(filter_uw=False, min_time=0.0)\n\nNotes\n-----\n- Call after `uw.timing.start()` and your computation\n- Filtered view helps identify UW3 performance bottlenecks\n- For comprehensive PETSc profiling, use `uw.timing.print_table()` or `filter_uw=False`", - "harvested_comments": [ - "... do work ...", - "Get UW3-focused summary", - "Get all events (including PETSc internals)", - "Collect all events first to calculate total time", - "UW3 event patterns (customize based on actual event naming)" + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "print_summary", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 607, - "signature": "(filter_uw = True, min_time = 0.001, sort_by = 'time', max_events = 50)", + "name": "_eta_for_tensor", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3240, + "signature": "(self, integrator_mode, apply_yield)", "parameters": [ { - "name": "filter_uw", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "min_time", - "type_hint": null, - "default": "0.001", - "description": "" - }, - { - "name": "sort_by", + "name": "integrator_mode", "type_hint": null, - "default": "'time'", + "default": null, "description": "" }, { - "name": "max_events", + "name": "apply_yield", "type_hint": null, - "default": "50", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Print user-friendly timing summary table.\n\nDisplays a clean, focused table of timing results for UW3 operations.\nMuch more readable than the full PETSc log for typical users.\n\nParameters\n----------\nfilter_uw : bool, optional\n If True (default), show only UW3 operations. If False, show all events.\nmin_time : float, optional\n Minimum time (seconds) for an event to be displayed. Default 0.001 (1ms).\nsort_by : str, optional\n Sort events by: 'time' (default), 'count', or 'name'.\nmax_events : int, optional\n Maximum number of events to display. Default 50.\n\nExample\n-------\n>>> import underworld3 as uw\n>>> uw.timing.start()\n>>> # ... run simulation ...\n>>>\n>>> # Quick UW3-focused summary\n>>> uw.timing.print_summary()\n>>>\n>>> # Detailed view with all events\n>>> uw.timing.print_summary(filter_uw=False, max_events=100)\n>>>\n>>> # Show top 10 most-called operations\n>>> uw.timing.print_summary(sort_by='count', max_events=10)\n\nNotes\n-----\n- For full PETSc profiling details, use `uw.timing.print_table()`\n- This function focuses on high-level UW3 operations\n- Perfect for quick performance checks in notebooks", - "harvested_comments": [ - "... run simulation ...", - "Quick UW3-focused summary", - "Detailed view with all events", - "Show top 10 most-called operations", - "Only rank 0 prints" - ], + "existing_docstring": "Return ``(eta_0, eta_1_eff)`` for tensor build, parameterised\nby integrator mode and whether to apply yield clipping.\n\n- ``integrator_mode='bdf'``: \u03b7\u2080, \u03b7\u2081 are VE-effective (c\u2080-baked\n \u0394t scaling \u2014 needed for BDF's E_eff structure).\n- ``integrator_mode='etd'``: \u03b7\u2080, \u03b7\u2081 are raw (time factor lives\n in \u03b1/\u03c6 symbolically). Used for both ETD-1 and ETD-2 \u2014 the\n C tensor is identical; only the symbolic E_eff differs (via\n ``self._order``).\n- ``apply_yield=True``: softmin/min/harmonic clip on \u03b7\u2081_eff.\n- ``apply_yield=False``: no clipping (use this for the ETD-VE\n branch of the hybrid integrator, where the bulk is\n structurally non-yieldable so clipping is a no-op anyway).", + "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "create_quantity", + "name": "_assemble_c_tensor", "kind": "method", - "file": "src/underworld3/units.py", - "line": 60, - "signature": "(self, value, units)", + "file": "src/underworld3/constitutive_models.py", + "line": 3285, + "signature": "(self, eta_0, eta_1_eff)", "parameters": [ { - "name": "value", + "name": "eta_0", "type_hint": null, "default": null, "description": "" }, { - "name": "units", + "name": "eta_1_eff", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create a Pint quantity.", + "existing_docstring": "Build the anisotropic rank-4 tensor from ``(eta_0, eta_1_eff)``.\n\nLoop body identical to :py:meth:`_build_c_tensor_ve`; refactored\ninto a helper so the hybrid path can call it twice (BDF tensor\nwith yield clip, ETD tensor without) without code duplication.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_PintHelper", - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "get_units", + "name": "_build_c_tensor", "kind": "method", - "file": "src/underworld3/units.py", - "line": 66, - "signature": "(self, quantity)", - "parameters": [ - { - "name": "quantity", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/constitutive_models.py", + "line": 3318, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Build the anisotropic tensor(s) for the active integrator.\n\n- ``'bdf'`` / ``'etd'``: single tensor ``self._c``.\n- ``'hybrid'``: two tensors ``self._c_bdf`` (yield-clipped) and\n ``self._c_etd`` (no clip \u2014 bulk is non-yieldable). The flux\n blend ``w\u00b7\u03c3_BDF + (1-w)\u00b7\u03c3_ETD`` happens in :py:meth:`stress`.", + "harvested_comments": [ + "default for any callers reading self._c" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false + }, + { + "name": "_build_c_tensor_ve", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3369, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Get units from a Pint quantity.", + "existing_docstring": "Build anisotropic tensor with VE \u03b7\u2081 only (no yield).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_PintHelper", - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "check_dimensionality", + "name": "_q_with", "kind": "method", - "file": "src/underworld3/units.py", - "line": 70, - "signature": "(self, q1, q2)", + "file": "src/underworld3/constitutive_models.py", + "line": 3438, + "signature": "(self, c, edot)", "parameters": [ { - "name": "q1", + "name": "c", "type_hint": null, "default": null, "description": "" }, { - "name": "q2", + "name": "edot", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Check if two quantities have compatible dimensionality.", + "existing_docstring": "Apply a given rank-4 tensor to a strain rate (helper for\nthe hybrid flux that needs to contract two distinct C tensors\nin one ``stress()`` call).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_PintHelper", - "is_public": true + "parent_class": "TransverseIsotropicVEPFlowModel", + "is_public": false }, { - "name": "get_dimensionality", + "name": "_update_history_coefficients", "kind": "method", - "file": "src/underworld3/units.py", - "line": 74, - "signature": "(self, quantity)", - "parameters": [ - { - "name": "quantity", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/constitutive_models.py", + "line": 3625, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Get dimensionality of a quantity.", + "existing_docstring": "Update ``(\u03b1_\u22a5, \u03c6_\u22a5)`` only \u2014 the matrix branch.\n\n``(\u03b1_\u2225, \u03c6_\u2225)`` are inlined per-quadrature in :py:meth:`stress`.\nPicks ``\u03c4_\u22a5 = \u03b7_0/\u03bc`` (raw matrix viscosity).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "_PintHelper", - "is_public": true - }, - { - "name": "check_units_consistency", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 510, - "signature": "(*expressions) -> bool", - "parameters": [ - { - "name": "*expressions", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": "bool", - "existing_docstring": "Check if multiple expressions have consistent units for addition/comparison.\n\nThis function validates that all provided expressions have the same dimensionality,\nwhich is required for addition, subtraction, and comparison operations.\n\nArgs:\n *expressions: Any number of expressions, quantities, or unit-aware objects\n\nReturns:\n bool: True if all expressions have consistent units, False otherwise\n\nRaises:\n DimensionalityError: If expressions have inconsistent units\n NoUnitsError: If some expressions have units and others don't\n\nExamples:\n >>> velocity1 = EnhancedMeshVariable(\"v1\", mesh, 2, units=\"m/s\")\n >>> velocity2 = EnhancedMeshVariable(\"v2\", mesh, 2, units=\"km/h\")\n >>> pressure = EnhancedMeshVariable(\"p\", mesh, 1, units=\"Pa\")\n\n >>> check_units_consistency(velocity1, velocity2) # True - both velocities\n >>> check_units_consistency(velocity1, pressure) # False - different dimensions", - "harvested_comments": [ - "True - both velocities", - "False - different dimensions", - "Extract units info from all expressions", - "Check if all have units or all don't have units", - "All are unitless - consistent" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "get_dimensionality", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 567, - "signature": "(expression) -> Optional[Any]", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": "Optional[Any]", - "existing_docstring": "Get the dimensionality of an expression or quantity.\n\nArgs:\n expression: Expression, quantity, or unit-aware object\n\nReturns:\n Dimensionality representation (backend-specific) or None if no units\n\nExamples:\n >>> velocity = EnhancedMeshVariable(\"velocity\", mesh, 2, units=\"m/s\")\n >>> dims = get_dimensionality(velocity)\n >>> print(dims) # [length] / [time]", - "harvested_comments": [ - "[length] / [time]" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "get_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 591, - "signature": "(expression, simplify: bool = False) -> Optional[Any]", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "simplify", - "type_hint": "bool", - "default": "False", - "description": "" - } - ], - "returns": "Optional[Any]", - "existing_docstring": "Get the units of an expression or quantity.\n\nThis is the unified, public API for extracting units from any object type\n(variables, quantities, SymPy expressions, etc.). It replaces the previous\n`units_of()` function and the internal `function.unit_conversion.get_units()`\nfor a clean, single API surface.\n\nIMPORTANT: This function ALWAYS returns Pint Unit objects (or None), never strings.\nWe accept strings for INPUT (user convenience), but always return Pint objects.\n\nArgs:\n expression: Expression, quantity, or unit-aware object\n simplify: If True, convert mixed units to simplified base SI units.\n For example, `megayear ** 0.5 * meter / second ** 0.5`\n simplifies to just `meter`. Default: False.\n\nReturns:\n Pint Unit object or None if no units\n\nExamples:\n >>> velocity = uw.discretisation.MeshVariable(\"velocity\", mesh, 2, units=\"m/s\")\n >>> units = uw.get_units(velocity)\n >>> print(units) # \n\n >>> # Mixed units from composite expressions\n >>> th2 = uw.expression(\"th2\", ((2 * kappa * t_now))**0.5)\n >>> uw.get_units(th2) # megayear ** 0.5 * meter / second ** 0.5\n >>> uw.get_units(th2, simplify=True) # meter (simplified!)", - "harvested_comments": [ - "", - "Mixed units from composite expressions", - "megayear ** 0.5 * meter / second ** 0.5", - "meter (simplified!)", - "Optionally simplify mixed units to base SI units" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "non_dimensionalise", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 645, - "signature": "(expression, model = None) -> Any", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "model", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": "Any", - "existing_docstring": "Convert expression to non-dimensional form using model reference scales.\n\nThis function uses dimensional analysis to compute appropriate scaling factors\nfrom the model's reference quantities, then divides the expression by those\nscales to produce dimensionless values. Dimensionality information is preserved\nto enable re-dimensionalization.\n\nProtocol-based approach works with:\n- MeshVariable/SwarmVariable (via .non_dimensional_value() method)\n- UWQuantity objects (extracts dimensionality, computes scale)\n- UnitAwareArray (extracts dimensionality from units)\n- Plain numbers (pass through unchanged)\n\nArgs:\n expression: Expression, quantity, or unit-aware object to non-dimensionalise\n model: Model instance with reference quantities (uses default if None)\n\nReturns:\n Non-dimensional value(s) with preserved dimensionality metadata\n\nRaises:\n NoUnitsError: If expression has no units and model has reference quantities\n ValueError: If model has no reference quantities\n\nExamples:\n >>> # With variables (uses existing method)\n >>> velocity_var = MeshVariable(\"v\", mesh, 2, units=\"m/s\")\n >>> nondim_v = non_dimensionalise(velocity_var)\n\n >>> # With UWQuantity\n >>> velocity_qty = uw.quantity(5.0, \"cm/year\")\n >>> nondim_qty = non_dimensionalise(velocity_qty, model)\n >>> # Result is dimensionless but remembers it was velocity\n\n >>> # With plain number (no model reference quantities)\n >>> plain_value = 2.5\n >>> result = non_dimensionalise(plain_value) # Returns 2.5", - "harvested_comments": [ - "With variables (uses existing method)", - "With UWQuantity", - "Result is dimensionless but remembers it was velocity", - "With plain number (no model reference quantities)", - "Returns 2.5" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPSplitFlowModel", + "is_public": false }, { - "name": "show_nondimensional_form", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 883, - "signature": "(expression, model = None)", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "model", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "name": "_eta_par_eff", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3653, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Display the non-dimensionalized form of a complex expression.\n\nThis function unwraps the expression (expanding UW wrappers to SymPy),\napplies non-dimensional scaling, and returns the result for inspection.\nUseful for seeing what will actually be compiled into C code when\nscaling is active.\n\nArgs:\n expression: Any SymPy expression, UW expression, or variable\n model: Model instance with reference quantities (uses default if None)\n\nReturns:\n SymPy expression with non-dimensional scaling applied\n\nExamples:\n >>> # See what the Stokes flux looks like non-dimensionally\n >>> flux = stokes.constitutive_model.flux\n >>> nondim_flux = uw.show_nondimensional_form(flux)\n >>> print(nondim_flux) # Should show values close to 1.0, not 1e21\n\n >>> # Check a parameter\n >>> viscosity = model.Parameters.shear_viscosity_0\n >>> print(uw.show_nondimensional_form(viscosity)) # Should be ~1.0", - "harvested_comments": [ - "See what the Stokes flux looks like non-dimensionally", - "Should show values close to 1.0, not 1e21", - "Check a parameter", - "Should be ~1.0", - "Check if scaling is active" + "existing_docstring": "Yield-clipped ``\u03b7_\u2225_eff`` \u2014 same softmin/min/harmonic as parent.\n\nFor ETD the base is the raw ``\u03b7_1`` (no VE pre-clip); the yield\nenvelope is then applied via the configured yield_mode.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPSplitFlowModel", + "is_public": false }, { - "name": "dimensionalise", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 929, - "signature": "(expression, target_dimensionality = None, model = None) -> Any", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "target_dimensionality", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "model", - "type_hint": null, - "default": "None", - "description": "" - } - ], - "returns": "Any", - "existing_docstring": "Restore dimensional form to non-dimensional values using model reference scales.\n\nThis is the companion function to non_dimensionalise(). It multiplies dimensionless\nvalues by the appropriate reference scale to restore their dimensional form.\n\nThe function can operate in two modes:\n1. **Auto mode**: Extract dimensionality from the expression itself (if preserved)\n2. **Explicit mode**: Use provided target_dimensionality\n\nArgs:\n expression: Non-dimensional value (UWQuantity, UnitAwareArray, or plain number)\n with preserved dimensionality metadata\n target_dimensionality: Optional dict specifying target dimensionality\n (Pint format: e.g., {'[length]': 1, '[time]': -1} for velocity)\n If None, uses dimensionality from expression\n model: Model instance with reference quantities (uses default if None)\n\nReturns:\n Dimensional quantity with appropriate units\n\nRaises:\n ValueError: If no dimensionality information available\n ValueError: If model has no reference quantities\n\nExamples:\n >>> # Auto mode - dimensionality preserved from non_dimensionalise()\n >>> velocity_qty = uw.quantity(5.0, \"cm/year\")\n >>> nondim_vel = non_dimensionalise(velocity_qty, model)\n >>> # nondim_vel remembers it was velocity\n >>> dimensional_vel = dimensionalise(nondim_vel, model=model)\n >>> # Result has appropriate units based on model scales\n\n >>> # Explicit mode - specify dimensionality\n >>> plain_value = 2.5 # dimensionless number\n >>> velocity_dimensionality = {'[length]': 1, '[time]': -1}\n >>> velocity = dimensionalise(plain_value, velocity_dimensionality, model)\n >>> # Result is 2.5 * (length_scale / time_scale)\n\n >>> # With arrays\n >>> nondim_array = UnitAwareArray([1.0, 2.0, 3.0],\n ... units=\"dimensionless\",\n ... dimensionality={'[length]': 1})\n >>> dimensional_array = dimensionalise(nondim_array, model=model)", + "name": "_eta_par_eff_lagged", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3680, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Yield-clipped ``\u03b7_\u2225_eff`` using the **lagged** strain rate\n(``forcing_star``, projected post-solve) instead of the current\n``E_eff``.\n\nSame softmin/harmonic/min envelope as :py:meth:`_eta_par_eff`,\nbut the plastic estimate ``vp_eff_lag = \u03c4_y/(2|\u03b3\u0307*|)`` uses the\n*previous step's* fault-plane shear magnitude. This is the\nproper \"lag \u03b7_\u2225_eff\": elastic regime \u2192 \u03b7_1_raw (low |\u03b3\u0307*|);\nyielded regime \u2192 ``\u03c4_y/(2|\u03b3\u0307*|)`` (saturated stress, large rate).\nUsing the parent's E_eff-based formula here would couple \u03b1_\u2225\nback into the current Newton iterate, which collapses to a\n1-iteration trivial residual (over-damping). Holding the rate\nfixed at the post-solve value breaks that feedback.", "harvested_comments": [ - "Auto mode - dimensionality preserved from non_dimensionalise()", - "nondim_vel remembers it was velocity", - "Result has appropriate units based on model scales", - "Explicit mode - specify dimensionality", - "dimensionless number" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "simplify_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1086, - "signature": "(expression) -> Any", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - } + "no history yet \u2014 fall back to current", + "Lagged plastic estimate: |\u03b3\u0307*_\u2225| from forcing_star + director", + "Strip Pint from the \u03b5_min floor (forcing_star is unitless storage)" ], - "returns": "Any", - "existing_docstring": "Simplify and cancel units in an expression.\n\nThis function performs dimensional analysis to simplify unit expressions,\ncanceling common factors and reducing to fundamental units.\n\nArgs:\n expression: Expression with units to simplify\n\nReturns:\n Expression with simplified units\n\nExamples:\n >>> # Force per area = pressure\n >>> force_per_area = force / area # N/m\u00b2\n >>> simplified = simplify_units(force_per_area) # Pa", - "harvested_comments": [ - "Force per area = pressure", - "This would need backend-specific implementation", - "For now, return the original expression" + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "parent_class": "TransverseIsotropicVEPSplitFlowModel", + "is_public": false }, { - "name": "create_quantity", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1115, - "signature": "(value, units: Union[str, Any], backend: Optional[str] = None) -> Any", + "name": "_build_split_c_tensors", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3743, + "signature": "(self, eta_perp, eta_par)", "parameters": [ { - "name": "value", + "name": "eta_perp", "type_hint": null, "default": null, "description": "" }, { - "name": "units", - "type_hint": "Union[str, Any]", + "name": "eta_par", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "backend", - "type_hint": "Optional[str]", - "default": "None", - "description": "" } ], - "returns": "Any", - "existing_docstring": "Create a dimensional quantity from a value and units.\n\nArgs:\n value: Numeric value or array\n units: Units specification (string or units object)\n backend: Backend to use ('pint' or 'sympy'), defaults to 'pint'\n\nReturns:\n Dimensional quantity\n\nExamples:\n >>> velocity_qty = create_quantity([1.0, 2.0], \"m/s\")\n >>> pressure_qty = create_quantity(101325, \"Pa\")", + "returns": null, + "existing_docstring": "Build ``C_\u22a5 = 2\u00b7\u03b7_\u22a5\u00b7P_\u22a5`` and ``C_\u2225 = 2\u00b7\u03b7_\u2225\u00b7P_\u2225``.\n\nIdentical loop structure to :py:meth:`_build_c_tensor`, but\neach tensor isolates one projector by zeroing the other\nviscosity coefficient.", "harvested_comments": [ - "backend parameter is deprecated - Pint is the only supported backend" + "2\u00b7\u03b7_\u22a5\u00b7P_\u22a5 = 2\u00b7\u03b7_\u22a5\u00b7(I - K)", + "2\u00b7\u03b7_\u2225\u00b7P_\u2225 = 2\u00b7\u03b7_\u2225\u00b7K", + "Same guard as parent _build_c_tensor \u2014 sympy", + "NDimArray.__setitem__ refuses iterable RHS." ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "TransverseIsotropicVEPSplitFlowModel", + "is_public": false }, { - "name": "convert_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1138, - "signature": "(quantity, target_units: Union[str, Any]) -> Any", + "name": "_setup_shared_unknowns", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3965, + "signature": "(self, constitutive_models, unknowns)", "parameters": [ { - "name": "quantity", + "name": "constitutive_models", "type_hint": null, "default": null, "description": "" }, { - "name": "target_units", - "type_hint": "Union[str, Any]", - "default": null, - "description": "" - } - ], - "returns": "Any", - "existing_docstring": "Convert quantity to different units.\n\nThis is the SINGLE SOURCE OF TRUTH for unit conversion in UW3.\nAll .to() methods on unit-aware types should delegate to this function.\n\nHandles:\n- UWQuantity \u2192 returns new UWQuantity with converted value\n- UWexpression \u2192 returns new UWexpression with converted value\n- UnitAwareArray \u2192 returns new UnitAwareArray with converted values\n- Pint Quantity \u2192 returns converted Pint Quantity\n\nArgs:\n quantity: Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint)\n target_units: Target units for conversion (str or Pint Unit)\n\nReturns:\n Same type as input, converted to target units\n\nRaises:\n DimensionalityError: If units are not compatible for conversion\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> velocity = uw.quantity(10, \"m/s\")\n >>> velocity_kmh = uw.convert_units(velocity, \"km/h\")\n >>> print(velocity_kmh) # 36.0 [kilometer / hour]\n\n >>> radius = uw.expression(\"r\", uw.quantity(6370, \"km\"))\n >>> radius_m = uw.convert_units(radius, \"m\")\n >>> print(radius_m.value) # 6370000.0", - "harvested_comments": [ - "36.0 [kilometer / hour]", - "Get current units info", - "Parse target units if string", - "Check dimensional compatibility", - "Create test quantities to check compatibility" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "to_base_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1272, - "signature": "(quantity) -> Any", - "parameters": [ - { - "name": "quantity", + "name": "unknowns", "type_hint": null, "default": null, "description": "" } ], - "returns": "Any", - "existing_docstring": "Convert quantity to SI base units.\n\nThis is a convenience function that converts any unit-aware quantity\nto its SI base unit representation.\n\nArgs:\n quantity: Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint)\n\nReturns:\n Same type as input, converted to SI base units\n\nRaises:\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> velocity = uw.quantity(36, \"km/h\")\n >>> velocity_si = uw.to_base_units(velocity)\n >>> print(velocity_si) # 10.0 [meter / second]\n\n >>> radius = uw.expression(\"r\", uw.quantity(6370, \"km\"))\n >>> radius_si = uw.to_base_units(radius)\n >>> print(radius_si.value) # 6370000.0", + "returns": null, + "existing_docstring": "Ensure all constituent models share the solver's authoritative unknowns.\nThis is critical for proper stress history management.", "harvested_comments": [ - "10.0 [meter / second]", - "Get current units info", - "=== Handle different input types ===", - "1. UWQuantity - use its native .to_base_units() method", - "2. UWexpression - convert the wrapped value" + "Share solver's unknowns - this gives access to composite D(F)/Dt history", + "Validation: Ensure sharing worked correctly", + "For elastic models, verify DFDt access" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MultiMaterialConstitutiveModel", + "is_public": false }, { - "name": "to_reduced_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1366, - "signature": "(quantity) -> Any", + "name": "_validate_model_compatibility", + "kind": "method", + "file": "src/underworld3/constitutive_models.py", + "line": 3983, + "signature": "(self, models: list) -> bool", "parameters": [ { - "name": "quantity", - "type_hint": null, + "name": "models", + "type_hint": "list", "default": null, "description": "" } ], - "returns": "Any", - "existing_docstring": "Simplify units by canceling common factors.\n\nThis is useful for complex unit expressions like (kg * m / s\u00b2) / (kg / m\u00b3)\nwhich would simplify to m\u2074 / s\u00b2.\n\nArgs:\n quantity: Quantity to simplify (UWQuantity, UWexpression, UnitAwareArray, Pint)\n\nReturns:\n Same type as input, with simplified units\n\nRaises:\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> force = uw.quantity(100, \"kg*m/s**2\")\n >>> force_simplified = uw.to_reduced_units(force)\n >>> print(force_simplified) # 100.0 [newton]", + "returns": "bool", + "existing_docstring": "Ensure all constituent models are compatible for flux averaging.\n\nChecks:\n- Same u_dim (scalar vs vector problem compatibility)\n- Same spatial dimension (2D/3D consistency)\n- Compatible flux tensor shapes\n- All models properly initialized", "harvested_comments": [ - "100.0 [newton]", - "Get current units info", - "=== Handle different input types ===", - "1. UWQuantity - use its native .to_reduced_units() method", - "2. UWexpression - convert the wrapped value" + "Validate model is properly initialized" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "MultiMaterialConstitutiveModel", + "is_public": false }, { - "name": "to_compact", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1452, - "signature": "(quantity) -> Any", + "name": "__eq__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 107, + "signature": "(self, other)", "parameters": [ { - "name": "quantity", + "name": "other", "type_hint": null, "default": null, "description": "" } ], - "returns": "Any", - "existing_docstring": "Convert quantity to most human-readable unit representation.\n\nThis automatically chooses unit prefixes (kilo, mega, micro, etc.)\nto make the number more readable. For example, 0.001 km becomes 1 m.\n\nArgs:\n quantity: Quantity to make compact (UWQuantity, UWexpression, UnitAwareArray, Pint)\n\nReturns:\n Same type as input, with compact units\n\nRaises:\n NoUnitsError: If quantity has no units\n\nExamples:\n >>> length = uw.quantity(0.001, \"km\")\n >>> length_compact = uw.to_compact(length)\n >>> print(length_compact) # 1.0 [meter]\n\n >>> big_length = uw.quantity(1e9, \"m\")\n >>> big_compact = uw.to_compact(big_length)\n >>> print(big_compact) # 1.0 [gigameter]", + "returns": null, + "existing_docstring": "Equal to the original BaseScalar from the SAME coordinate system.\n\nThis is the key to making sympy.diff() work - when SymPy checks if\nthe differentiation variable matches symbols in the expression,\nthis makes UWCoordinate match the original BaseScalar.\n\nIMPORTANT: We only match BaseScalars from the SAME mesh's coordinate\nsystem using object identity (`is`), not name comparison. This prevents\ncross-mesh coordinate pollution where coordinates from different meshes\nwould be treated as equal due to having the same name \"N.x\".", "harvested_comments": [ - "1.0 [meter]", - "1.0 [gigameter]", - "Get current units info", - "=== Handle different input types ===", - "1. UWQuantity - use its native .to_compact() method" + "DON'T fall back to BaseScalar.__eq__ which compares by name!", + "This caused cross-mesh coordinate pollution (issue discovered 2025-12-15)" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "get_scaling_coefficients", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1542, - "signature": "() -> Dict[str, Any]", + "name": "__hash__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 129, + "signature": "(self)", "parameters": [], - "returns": "Dict[str, Any]", - "existing_docstring": "Get the current scaling coefficients used for non-dimensionalisation.\n\nReturns:\n Dictionary of scaling coefficients for fundamental dimensions\n\nExamples:\n >>> coeffs = get_scaling_coefficients()\n >>> print(coeffs['[length]']) # 1.0 meter\n >>> print(coeffs['[time]']) # 1.0 year", - "harvested_comments": [ - "Use the existing scaling module" - ], + "returns": null, + "existing_docstring": "Hash must match _original_base_scalar's hash for SymPy compatibility.\n\nSince __eq__ returns True when comparing to the original BaseScalar,\nwe MUST return the same hash (Python requirement: a == b \u2192 hash(a) == hash(b)).\n\nThis is critical for SymPy's differentiation to work correctly -\nsympy.diff() uses hash-based lookup to find matching symbols.\n\nNote: Cross-mesh coordinate collision is prevented by __eq__ checking\nobject identity of _original_base_scalar, not by different hashes.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "set_scaling_coefficients", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1560, - "signature": "(coefficients: Dict[str, Any]) -> None", + "name": "_numpycode", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 144, + "signature": "(self, printer)", "parameters": [ { - "name": "coefficients", - "type_hint": "Dict[str, Any]", + "name": "printer", + "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Set custom scaling coefficients for non-dimensionalisation.\n\nArgs:\n coefficients: Dictionary mapping dimension names to scaling quantities\n\nExamples:\n >>> coeffs = get_scaling_coefficients()\n >>> coeffs['[length]'] = create_quantity(1000, \"km\") # Geological scale\n >>> coeffs['[time]'] = create_quantity(1e6, \"year\") # Geological time\n >>> set_scaling_coefficients(coeffs)", + "returns": null, + "existing_docstring": "NumPy code generation for lambdify().\n\nReturns a unique dummy name that lambdify will map to array columns.\nUses the coordinate's internal index to generate a consistent name.", "harvested_comments": [ - "Geological scale", - "Geological time", - "This would need to update the scaling module's global coefficients" + "Use the short coordinate name (x, y, z) based on axis index" ], "status": "partial", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "is_dimensionless", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1578, - "signature": "(expression) -> bool", + "name": "_lambdacode", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 155, + "signature": "(self, printer)", "parameters": [ { - "name": "expression", + "name": "printer", "type_hint": null, "default": null, "description": "" } ], - "returns": "bool", - "existing_docstring": "Check if expression is dimensionless.", + "returns": null, + "existing_docstring": "Lambda code generation.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "has_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1583, - "signature": "(expression) -> bool", + "name": "_pythoncode", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 160, + "signature": "(self, printer)", "parameters": [ { - "name": "expression", + "name": "printer", "type_hint": null, "default": null, "description": "" } ], - "returns": "bool", - "existing_docstring": "Check if expression has units.", + "returns": null, + "existing_docstring": "Python code generation.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "same_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1588, - "signature": "(expr1, expr2) -> bool", - "parameters": [ - { - "name": "expr1", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "expr2", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": "bool", - "existing_docstring": "Check if two expressions have the same units.", + "name": "_base_scalar", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 203, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Backward compatibility: return the original BaseScalar.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "is_velocity", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1596, - "signature": "(expression) -> bool", - "parameters": [ - { - "name": "expression", - "type_hint": null, - "default": null, - "description": "" - } - ], - "returns": "bool", - "existing_docstring": "Check if expression has velocity dimensions [length]/[time].", - "harvested_comments": [ - "This would need backend-specific dimensionality checking", - "For now, check string representation" - ], - "status": "minimal", + "name": "_ccodestr", + "kind": "property", + "file": "src/underworld3/coordinates.py", + "line": 224, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Delegate C code string to the original BaseScalar.\n\nThe mesh sets _ccodestr on the original N.x, N.y, N.z objects\nfor JIT code generation (e.g., \"petsc_x[0]\", \"petsc_x[1]\").\nUWCoordinate must expose the same attribute for JIT to work.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "is_pressure", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1606, - "signature": "(expression) -> bool", + "name": "_ccodestr", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 235, + "signature": "(self, value)", "parameters": [ { - "name": "expression", + "name": "value", "type_hint": null, "default": null, "description": "" } ], - "returns": "bool", - "existing_docstring": "Check if expression has pressure dimensions [mass]/([length]\u22c5[time]\u00b2).", - "harvested_comments": [ - "This would need backend-specific dimensionality checking" - ], + "returns": null, + "existing_docstring": "Allow setting _ccodestr (propagates to original BaseScalar).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "validate_expression_units", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1616, - "signature": "(expression, expected_units: Union[str, Any]) -> bool", + "name": "_ccode", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 239, + "signature": "(self, printer, **kwargs)", "parameters": [ { - "name": "expression", + "name": "printer", "type_hint": null, "default": null, "description": "" }, { - "name": "expected_units", - "type_hint": "Union[str, Any]", + "name": "**kwargs", + "type_hint": null, "default": null, "description": "" } ], - "returns": "bool", - "existing_docstring": "Validate that an expression has the expected units.\n\nArgs:\n expression: Expression to validate\n expected_units: Expected units (string or units object)\n\nReturns:\n True if units match, False otherwise\n\nRaises:\n NoUnitsError: If expression has no units but units are expected", + "returns": null, + "existing_docstring": "C code representation for JIT compilation.\n\nThe SymPy CCodePrinter looks for this method on symbols.\nWe delegate to the original BaseScalar's _ccodestr.", "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "UWCoordinate", + "is_public": false }, { - "name": "assert_dimensionality", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1641, - "signature": "(value, expected_dimensionality: str, value_name: str = 'value', allow_dimensionless: bool = True, strict: bool = False) -> None", + "name": "_latex", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 251, + "signature": "(self, printer)", "parameters": [ { - "name": "value", + "name": "printer", "type_hint": null, "default": null, "description": "" - }, - { - "name": "expected_dimensionality", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "value_name", - "type_hint": "str", - "default": "'value'", - "description": "" - }, - { - "name": "allow_dimensionless", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "strict", - "type_hint": "bool", - "default": "False", - "description": "" } ], - "returns": "None", - "existing_docstring": "Assert that a value has the expected dimensionality.\n\nThis is a general type-safety gatekeeper that validates physical dimensionality\nat various points in the code. Complements get_dimensionality() by providing\nenforcement rather than just inspection.\n\nArgs:\n value: The value to validate (quantity, expression, variable, array, etc.)\n expected_dimensionality: Expected dimensionality as a string\n - Specific dimensionality: \"[length]\", \"[length]/[time]\", \"[mass]*[length]/[time]**2\"\n - Dimensionless: \"dimensionless\" or \"\"\n value_name: Name of the value being validated (for error messages)\n allow_dimensionless: If True, accept dimensionless values even when dimensional\n expected (default: True, as dimensionless is valid for solver operations)\n strict: If True, raise error on dimensionless when dimensional expected\n (default: False, overrides allow_dimensionless)\n\nRaises:\n DimensionalityError: If dimensionality doesn't match expected\n NoUnitsError: If strict=True and value is dimensionless when dimensional expected\n\nExamples:\n >>> # Validate coordinates have length dimensionality\n >>> coords = mesh.X.coords\n >>> assert_dimensionality(coords, \"[length]\", \"coordinates\")\n\n >>> # Validate velocity has correct dimensionality\n >>> velocity = uw.discretisation.MeshVariable(\"v\", mesh, 2, units=\"m/s\")\n >>> assert_dimensionality(velocity, \"[length]/[time]\", \"velocity\")\n\n >>> # Validate pressure\n >>> pressure = uw.quantity(1e5, \"Pa\")\n >>> assert_dimensionality(pressure, \"[mass]/([length]*[time]**2)\", \"pressure\")\n\n >>> # Accept dimensionless when dimensional expected (default)\n >>> dimensionless_coords = np.array([[0, 1], [1, 1]])\n >>> assert_dimensionality(dimensionless_coords, \"[length]\", \"coords\") # OK\n\n >>> # Strict mode: reject dimensionless when dimensional expected\n >>> assert_dimensionality(\n ... dimensionless_coords, \"[length]\", \"coords\", strict=True\n ... ) # Raises NoUnitsError", + "returns": null, + "existing_docstring": "LaTeX representation for SymPy printing.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWCoordinate", + "is_public": false + }, + { + "name": "_invalidate_cache", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 521, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mark coordinate cache as invalid (call when mesh coordinates change).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "GeographicCoordinateAccessor", + "is_public": false + }, + { + "name": "_compute_coordinates", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 525, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Compute geographic coordinates from Cartesian mesh coordinates.", "harvested_comments": [ - "Validate coordinates have length dimensionality", - "Validate velocity has correct dimensionality", - "Validate pressure", - "Accept dimensionless when dimensional expected (default)", - "Strict mode: reject dimensionless when dimensional expected" + "Get raw Cartesian coordinates from DM (avoids unit wrapping)", + "The mesh stores nondimensional coords when units are active", + "Get ellipsoid parameters", + "ellipsoid[\"a\"] is uw.quantity when units active, float (km) when not.", + "DM coordinates are nondimensional, so nondimensionalise to match." ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "GeographicCoordinateAccessor", + "is_public": false }, { - "name": "validate_coordinates_dimensionality", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1772, - "signature": "(coords) -> None", + "name": "__getitem__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 629, + "signature": "(self, idx)", "parameters": [ { - "name": "coords", + "name": "idx", "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Validate that coordinates have length dimensionality [L].\n\nThis function checks that the provided coordinates are either dimensionless\n(which is valid for solver operations) or have length dimensionality. It will\nraise an error if coordinates have the wrong dimensionality (e.g., time,\ntemperature, velocity).\n\nArgs:\n coords: Coordinate array to validate\n\nRaises:\n DimensionalityError: If coordinates have units but not length dimensionality\n\nExamples:\n >>> # Valid: dimensionless coords (for solvers)\n >>> validate_coordinates_dimensionality(np.array([[0, 1], [1, 1]]))\n\n >>> # Valid: coords with length units\n >>> coords = uw.function.UnitAwareArray([[0, 1000], [1000, 1000]], units=\"meter\")\n >>> validate_coordinates_dimensionality(coords)\n\n >>> # Invalid: coords with time units (would raise error)\n >>> time_coords = uw.quantity(5.0, \"second\")\n >>> validate_coordinates_dimensionality(time_coords) # Raises DimensionalityError", + "returns": null, + "existing_docstring": "Access symbolic geographic coordinates.\n\nReturns:\n \u03bb_lon, \u03bb_lat, \u03bb_d: Symbolic coordinates for use in equations", + "harvested_comments": [ + "mesh.geo[:]" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "GeographicCoordinateAccessor", + "is_public": false + }, + { + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 846, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "String representation showing available coordinates and basis vectors.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "GeographicCoordinateAccessor", + "is_public": false + }, + { + "name": "_invalidate_cache", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 980, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mark coordinate cache as invalid (call when mesh coordinates change).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SphericalCoordinateAccessor", + "is_public": false + }, + { + "name": "_compute_coordinates", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 984, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Compute spherical/polar coordinates from Cartesian mesh coordinates.", "harvested_comments": [ - "Valid: dimensionless coords (for solvers)", - "Valid: coords with length units", - "Invalid: coords with time units (would raise error)", - "Raises DimensionalityError", - "Check if coords have units" + "Get Cartesian coordinates", + "2D polar: (x, y) \u2192 (r, \u03b8)", + "3D spherical: (x, y, z) \u2192 (r, \u03b8, \u03c6)" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "SphericalCoordinateAccessor", + "is_public": false }, { - "name": "enforce_units_consistency", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1827, - "signature": "(*expressions) -> None", + "name": "__getitem__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 1072, + "signature": "(self, idx)", "parameters": [ { - "name": "*expressions", + "name": "idx", "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Enforce units consistency, raising an error if inconsistent.\n\nArgs:\n *expressions: Expressions that must have consistent units\n\nRaises:\n DimensionalityError: If units are inconsistent\n NoUnitsError: If some have units and others don't", + "returns": null, + "existing_docstring": "Access symbolic spherical/polar coordinates.\n\nReturns\n-------\ntuple or scalar\n For 3D: r, \u03b8, \u03c6 symbolic coordinates\n For 2D: r, \u03b8 symbolic coordinates", "harvested_comments": [ - "This already raises appropriate errors" + "mesh.spherical[:]" ], "status": "partial", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, - "is_public": true + "parent_class": "SphericalCoordinateAccessor", + "is_public": false }, { - "name": "require_units_if_active", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1847, - "signature": "(value, name: str, expected_dimensionality: str = None, default_unit: str = None) -> float", - "parameters": [ - { - "name": "value", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "expected_dimensionality", - "type_hint": "str", - "default": "None", - "description": "" - }, - { - "name": "default_unit", - "type_hint": "str", - "default": "None", - "description": "" - } + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 1253, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "String representation showing available coordinates and basis vectors.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "returns": "float", - "existing_docstring": "Validate input and return nondimensional value when units are active.\n\nThis is a gatekeeper function for mesh creation and similar contexts where:\n- When Model with units is active: inputs MUST have units (enforces explicitness)\n- When no units active: raw numbers are accepted\n\nThis prevents ambiguity in dimensional problems where a raw number like `400`\ncould mean meters, kilometers, or a nondimensional value.\n\nArgs:\n value: Input value (quantity, expression, or raw number)\n name: Parameter name for error messages (e.g., \"depth_max\")\n expected_dimensionality: Expected dimensionality string (e.g., \"[length]\")\n If provided, validates the input has correct dimensionality.\n default_unit: Default unit to assume for raw values when units not active.\n Only used for documentation in error messages.\n\nReturns:\n float: Nondimensional value (divided by appropriate reference scale)\n\nRaises:\n ValueError: If units are active but value has no units\n DimensionalityError: If value has wrong dimensionality\n\nExamples:\n >>> # With units active - requires quantity\n >>> model = uw.Model()\n >>> model.set_reference_quantities(length=uw.quantity(1000, \"km\"), ...)\n >>> depth = require_units_if_active(\n ... uw.quantity(400, \"km\"),\n ... \"depth_max\",\n ... expected_dimensionality=\"[length]\"\n ... )\n >>> depth # Returns 0.4 (400 km / 1000 km reference)\n\n >>> # Without units - raw numbers accepted\n >>> depth = require_units_if_active(400, \"depth_max\")\n >>> depth # Returns 400 (unchanged)\n\n >>> # Error case - units active but raw number provided\n >>> model.set_reference_quantities(...)\n >>> require_units_if_active(400, \"depth_max\") # Raises ValueError", + "parent_class": "SphericalCoordinateAccessor", + "is_public": false + }, + { + "name": "_apply_units_scaling", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 1792, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Mark coordinate system as scaled if model has units.", "harvested_comments": [ - "With units active - requires quantity", - "Returns 0.4 (400 km / 1000 km reference)", - "Without units - raw numbers accepted", - "Returns 400 (unchanged)", - "Error case - units active but raw number provided" + "Get the model from the mesh", + "Fall back to default model if mesh doesn't have one", + "Check if the model has units scaling enabled", + "No scaling to apply", + "Get fundamental scales from the model" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", + "is_public": false }, { - "name": "convert_angle_to_degrees", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 1947, - "signature": "(value, name: str = 'angle') -> float", + "name": "__getitem__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 1848, + "signature": "(self, idx)", "parameters": [ { - "name": "value", + "name": "idx", "type_hint": null, "default": null, "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": "'angle'", - "description": "" } ], - "returns": "float", - "existing_docstring": "Convert an angle value to degrees.\n\nAccepts:\n- Raw numbers: assumed to be degrees\n- Quantities with degree units: extracted as degrees\n- Quantities with radian units: converted to degrees\n\nArgs:\n value: Angle value (quantity or raw number)\n name: Parameter name for error messages\n\nReturns:\n float: Angle in degrees\n\nExamples:\n >>> convert_angle_to_degrees(45) # Raw number \u2192 45 degrees\n >>> convert_angle_to_degrees(uw.quantity(45, \"degree\")) # \u2192 45\n >>> convert_angle_to_degrees(uw.quantity(np.pi/4, \"radian\")) # \u2192 45", - "harvested_comments": [ - "Raw number \u2192 45 degrees", - "Raw number - assume degrees (conventional for geographic)", - "Has units - convert to degrees", - "pint-style quantity", - "UWQuantity with pint backend" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": true - }, - { - "name": "_Unknowns", - "kind": "class", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 53, - "signature": "class _Unknowns:", - "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Support mesh.X[0] for x-coordinate access.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "_Unknowns", - "kind": "class", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2123, - "signature": "class _Unknowns(SolverBaseClass._Unknowns):", + "name": "__iter__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 1852, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Support x, y = mesh.X unpacking.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__len__", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 32, - "signature": "def __init__(self, mesh):", + "file": "src/underworld3/coordinates.py", + "line": 1856, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Support len(mesh.X).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "_sympy_", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 57, - "signature": "def __init__(inner_self, _owning_solver):", + "file": "src/underworld3/coordinates.py", + "line": 1976, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Tell SymPy how to convert this object to a SymPy expression.\n\nNote: Uses _sympy_() protocol (not _sympify_()) for SymPy 1.14+ compatibility.\nThis is required for proper symbolic algebra in strict mode (matrix operations).\n\nThis enables CoordinateSystem to work seamlessly with SymPy operations\nlike diff, jacobian, and arithmetic operations.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__sympy__", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 832, - "signature": "def __init__(self,\n mesh : uw.discretisation.Mesh,\n u_Field : uw.discretisation.MeshVariable = None,\n degree: int = 2,\n verbose = False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", + "file": "src/underworld3/coordinates.py", + "line": 1988, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Alternative SymPy conversion protocol.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__getattr__", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1427, - "signature": "def __init__(self,\n mesh : uw.discretisation.Mesh,\n u_Field : uw.discretisation.MeshVariable = None,\n degree = 2,\n verbose = False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", - "parameters": [], + "file": "src/underworld3/coordinates.py", + "line": 2020, + "signature": "(self, name)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Delegate SymPy-specific attributes to the underlying symbolic matrix.\n\nThis allows CoordinateSystem to be used transparently in SymPy operations\nby forwarding attribute access to _X when the attribute doesn't exist\non CoordinateSystem itself.\n\nNote: When properties like 'geo' or 'spherical' raise AttributeError\n(because the coordinate system doesn't support them), Python falls\nthrough to __getattr__. We detect this and re-invoke the property\nto get the helpful error message.", + "harvested_comments": [ + "Prevent infinite recursion for _X access", + "For coordinate accessor properties, re-invoke the property to get", + "the helpful error message (they raise AttributeError with guidance)", + "Access the property descriptor directly to get its error message", + "Try to get the attribute from the underlying symbolic matrix" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__add__", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2126, - "signature": "def __init__(inner_self, owning_solver):", - "parameters": [], + "file": "src/underworld3/coordinates.py", + "line": 2053, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Support mesh.X + other.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__radd__", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2149, - "signature": "def __init__(self,\n mesh : underworld3.discretisation.Mesh,\n velocityField : Optional[underworld3.discretisation.MeshVariable] = None,\n pressureField : Optional[underworld3.discretisation.MeshVariable] = None,\n degree : Optional[int] = 2,\n p_continuous : Optional[bool] = True,\n verbose : Optional[bool] =False,\n DuDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n DFDt : Union[uw.systems.ddt.SemiLagrangian, uw.systems.ddt.Lagrangian] = None,\n ):", - "parameters": [], + "file": "src/underworld3/coordinates.py", + "line": 2057, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Support other + mesh.X.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__sub__", "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 338, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: Optional[uw.discretisation.MeshVariable] = None, degree: int = 2, verbose: bool = False)", + "file": "src/underworld3/coordinates.py", + "line": 2061, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "other", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "u_Field", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "verbose", - "type_hint": "bool", - "default": "False", - "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Initialize vector source term" - ], - "status": "none", + "existing_docstring": "Support mesh.X - other.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyVectorEquation", + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__rsub__", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 194, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, verbose = False, degree = 2, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "file": "src/underworld3/coordinates.py", + "line": 2065, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", - "default": null, - "description": "" - }, - { - "name": "u_Field", - "type_hint": "uw.discretisation.MeshVariable", - "default": "None", - "description": "" - }, - { - "name": "verbose", + "name": "other", "type_hint": null, - "default": "False", + "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Support other - mesh.X.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", + "is_public": false + }, + { + "name": "__mul__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 2069, + "signature": "(self, other)", + "parameters": [ { - "name": "degree", + "name": "other", "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "DuDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# Keep track", - "# Parent class will set up default values etc", - "default values for properties" - ], - "status": "none", + "existing_docstring": "Support mesh.X * other.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Poisson", + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "__rmul__", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 398, - "signature": "(self, mesh: uw.discretisation.Mesh, h_Field: Optional[uw.discretisation.MeshVariable] = None, v_Field: Optional[uw.discretisation.MeshVariable] = None, degree: int = 2, verbose = False, DuDt = None, DFDt = None)", + "file": "src/underworld3/coordinates.py", + "line": 2073, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "other", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "h_Field", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "v_Field", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "2", - "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Support other * mesh.X.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", + "is_public": false + }, + { + "name": "__truediv__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 2077, + "signature": "(self, other)", + "parameters": [ { - "name": "verbose", + "name": "other", "type_hint": null, - "default": "False", + "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Support mesh.X / other.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", + "is_public": false + }, + { + "name": "__rtruediv__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 2081, + "signature": "(self, other)", + "parameters": [ { - "name": "DuDt", + "name": "other", "type_hint": null, - "default": "None", + "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Support other / mesh.X.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "CoordinateSystem", + "is_public": false + }, + { + "name": "__pow__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 2085, + "signature": "(self, other)", + "parameters": [ { - "name": "DFDt", + "name": "other", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# Parent class will set up default values etc", - "default values for properties", - "# Set up the projection operator that", - "# solves the flow rate", - "If we add smoothing, it should be small" + "existing_docstring": "Support mesh.X ** other.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "none", + "parent_class": "CoordinateSystem", + "is_public": false + }, + { + "name": "__neg__", + "kind": "method", + "file": "src/underworld3/coordinates.py", + "line": 2089, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Support -mesh.X.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Darcy", + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "_cartesian_to_natural_coords", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 661, - "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: Optional[uw.discretisation.MeshVariable] = None, pressureField: Optional[uw.discretisation.MeshVariable] = None, degree: Optional[int] = 2, p_continuous: Optional[bool] = True, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "file": "src/underworld3/coordinates.py", + "line": 2592, + "signature": "(self, cartesian_coords)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "cartesian_coords", + "type_hint": null, "default": null, "description": "" - }, - { - "name": "velocityField", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "pressureField", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": "Optional[int]", - "default": "2", - "description": "" - }, - { - "name": "p_continuous", - "type_hint": "Optional[bool]", - "default": "True", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "DuDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Convert Cartesian coordinates to natural coordinate system.\n\nParameters\n----------\ncartesian_coords : numpy.ndarray\n Array of Cartesian coordinates (N_points, dim)\n\nReturns\n-------\nnumpy.ndarray\n Array of natural coordinates (N_points, dim)", "harvested_comments": [ - "Not used in Stokes, but may be used in NS, VE etc", - "User-facing operations are matrices / vectors by preference", - "by default, incompressibility constraint", - "this attrib records if we need to setup the problem (again)" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "For Cartesian, natural coordinates are the same as Cartesian", + "Convert (x, y) to (r, theta)", + "Convert (x, y, z) to (r, theta, z)", + "Convert (x, y, z) to (r, theta, phi)", + "colatitude (0 to pi)" ], - "parent_class": "SNES_Stokes", + "status": "complete", + "needs": [], + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "_create_cartesian_profile", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1203, - "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: Optional[uw.discretisation.MeshVariable] = None, pressureField: Optional[uw.discretisation.MeshVariable] = None, degree: Optional[int] = 2, order: Optional[int] = 2, p_continuous: Optional[bool] = True, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "file": "src/underworld3/coordinates.py", + "line": 2686, + "signature": "(self, profile_type, **params)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "profile_type", + "type_hint": null, "default": null, "description": "" }, { - "name": "velocityField", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "pressureField", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": "Optional[int]", - "default": "2", - "description": "" - }, - { - "name": "order", - "type_hint": "Optional[int]", - "default": "2", - "description": "" - }, - { - "name": "p_continuous", - "type_hint": "Optional[bool]", - "default": "True", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "DuDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", + "name": "**params", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Create profiles for Cartesian coordinate systems", "harvested_comments": [ - "DuDt Not used in VE, but may be in child classes", - "Stokes is parent (will not build DuDt or DFDt)", - "VE time-order" + "Horizontal line at specified y-position", + "Same for Cartesian", + "Vertical line at specified x-position", + "Diagonal line from start to end point" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_VE_Stokes", + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "_create_cylindrical_profile", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1374, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False)", + "file": "src/underworld3/coordinates.py", + "line": 2759, + "signature": "(self, profile_type, **params)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", - "default": null, - "description": "" - }, - { - "name": "u_Field", - "type_hint": "uw.discretisation.MeshVariable", - "default": "None", - "description": "" - }, - { - "name": "degree", + "name": "profile_type", "type_hint": null, - "default": "2", + "default": null, "description": "" }, { - "name": "verbose", + "name": "**params", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Create profiles for cylindrical coordinate systems", "harvested_comments": [ - "Default: project zero" + "Radial line at specified angle", + "Angle in radians", + "Convert to Cartesian coordinates", + "Natural coordinates", + "Tangential (circular arc) at specified radius" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Projection", + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", + "name": "_create_spherical_profile", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1493, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False)", + "file": "src/underworld3/coordinates.py", + "line": 2827, + "signature": "(self, profile_type, **params)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", - "default": null, - "description": "" - }, - { - "name": "u_Field", - "type_hint": "uw.discretisation.MeshVariable", - "default": "None", - "description": "" - }, - { - "name": "degree", + "name": "profile_type", "type_hint": null, - "default": "2", + "default": null, "description": "" }, { - "name": "verbose", + "name": "**params", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Create profiles for spherical coordinate systems", "harvested_comments": [ - "Default: project zero vector" + "Radial line at specified theta, phi", + "Colatitude (0 to pi)", + "Azimuth (-pi to pi)", + "Convert to Cartesian coordinates", + "Natural coordinates" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Vector_Projection", + "parent_class": "CoordinateSystem", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1638, - "signature": "(self, mesh: uw.discretisation.Mesh, tensor_Field: uw.discretisation.MeshVariable = None, scalar_Field: uw.discretisation.MeshVariable = None, degree = 2, verbose = False)", + "name": "_clear_gmsh_import_options", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 65, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Remove all ``dm_plex_gmsh_*`` import-scratch options from the global\nPETSc options database (idempotent).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_from_gmsh", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 95, + "signature": "(filename, comm = None, markVertices = False, useRegions = True, useMultipleTags = True)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "filename", + "type_hint": null, "default": null, "description": "" }, { - "name": "tensor_Field", - "type_hint": "uw.discretisation.MeshVariable", + "name": "comm", + "type_hint": null, "default": "None", "description": "" }, { - "name": "scalar_Field", - "type_hint": "uw.discretisation.MeshVariable", - "default": "None", + "name": "markVertices", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "degree", + "name": "useRegions", "type_hint": null, - "default": "2", + "default": "True", "description": "" }, { - "name": "verbose", + "name": "useMultipleTags", "type_hint": null, - "default": "False", + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Read a Gmsh .msh file from `filename`.\n\n:kwarg comm: Optional communicator to build the mesh on (defaults to\n COMM_WORLD).", + "harvested_comments": [ + "# NOTE: - this should be smart enough to serialise the msh conversion", + "# and then read back in parallel via h5. This is currently done", + "# by every gmesh mesh", + "This option allows objects to be in multiple physical groups", + "Rather than just the first one found." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Tensor_Projection", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1815, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable, V_fn: Union[uw.discretisation.MeshVariable, sympy.Basic], order: int = 1, restore_points_func: Callable = None, verbose = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "name": "_from_plexh5", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 160, + "signature": "(filename, comm = None, return_sf = False)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", - "default": null, - "description": "" - }, - { - "name": "u_Field", - "type_hint": "uw.discretisation.MeshVariable", - "default": null, - "description": "" - }, - { - "name": "V_fn", - "type_hint": "Union[uw.discretisation.MeshVariable, sympy.Basic]", + "name": "filename", + "type_hint": null, "default": null, "description": "" }, { - "name": "order", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "restore_points_func", - "type_hint": "Callable", + "name": "comm", + "type_hint": null, "default": "None", "description": "" }, { - "name": "verbose", + "name": "return_sf", "type_hint": null, "default": "False", "description": "" - }, - { - "name": "DuDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Should be a sympy function", - "# Parent class will set up default values etc", - "default values for properties", - "These are unique to the advection solver", - "## Setup the history terms ... This version should not build anything" - ], - "status": "none", + "existing_docstring": "Read a dmplex .h5 file from `filename` provided.\n\ncomm: Optional communicator to build the mesh on (defaults to\nCOMM_WORLD).", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2317, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: uw.discretisation.MeshVariable, order: int = 1, theta: float = 0.0, evalf: Optional[bool] = False, verbose = False, DuDt: Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "name": "_hierarchy_sidecar_name", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 193, + "signature": "(mesh_filename, level)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", - "default": null, - "description": "" - }, - { - "name": "u_Field", - "type_hint": "uw.discretisation.MeshVariable", + "name": "mesh_filename", + "type_hint": null, "default": null, "description": "" }, { - "name": "order", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "theta", - "type_hint": "float", - "default": "0.0", - "description": "" - }, - { - "name": "evalf", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "verbose", + "name": "level", "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "DuDt", - "type_hint": "Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Union[Eulerian_DDt, SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Filename for a coarse hierarchy sidecar of a mesh checkpoint.\n\nThe geometric-multigrid (FMG) hierarchy is persisted as a single extra\nsingle-DM HDF5 file holding the **coarsest** level (``level=0``) beside the\nmain mesh checkpoint \u2014 PETSc's ``HDF5_PETSC`` format does not support several\nDMPlex objects in one file. The intermediate coarse levels are rebuilt by\nrefinement on reload, so only ``mymesh.hierarchy.L0.h5`` is written. The\n``level`` argument is kept for forward-compatibility.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_vertex_map", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1916, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Build vertex index mapping between submesh and parent.\n\nUses coordinate matching at extraction time (before any\ndeformation). Cached permanently since topology doesn't change.", "harvested_comments": [ - "# Parent class will set up default values etc", - "default values for properties", - "These are unique to the advection solver", - "## Setup the history terms ... This version should not build anything", - "## by default - it's the template / skeleton" + "Build a KDTree directly on the coordinate arrays rather than", + "``self.X._get_kdtree()`` \u2014 ``mesh.X`` is a CoordinateSystem, which has", + "no ``_get_kdtree`` (that lives on MeshVariable/swarm vars), so the old", + "call raised AttributeError on every extract_region (UW3 issue #197).", + "This mirrors the proven inline path in ``extract_surface``: submesh" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_Diffusion", + "parent_class": "Mesh", "is_public": false }, { - "name": "__init__", + "name": "_re_extract_from_parent", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2724, - "signature": "(self, mesh: uw.discretisation.Mesh, velocityField: uw.discretisation.MeshVariable, pressureField: uw.discretisation.MeshVariable, rho: Optional[float] = 0.0, restore_points_func: Callable = None, order: Optional[int] = 2, p_continuous: Optional[bool] = False, verbose: Optional[bool] = False, DuDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None, DFDt: Union[SemiLagrangian_DDt, Lagrangian_DDt] = None)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 1975, + "signature": "(self, verbose = False)", "parameters": [ - { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", - "default": null, - "description": "" - }, - { - "name": "velocityField", - "type_hint": "uw.discretisation.MeshVariable", - "default": null, - "description": "" - }, - { - "name": "pressureField", - "type_hint": "uw.discretisation.MeshVariable", - "default": null, - "description": "" - }, - { - "name": "rho", - "type_hint": "Optional[float]", - "default": "0.0", - "description": "" - }, - { - "name": "restore_points_func", - "type_hint": "Callable", - "default": "None", - "description": "" - }, - { - "name": "order", - "type_hint": "Optional[int]", - "default": "2", - "description": "" - }, - { - "name": "p_continuous", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, { "name": "verbose", - "type_hint": "Optional[bool]", + "type_hint": null, "default": "False", "description": "" - }, - { - "name": "DuDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Union[SemiLagrangian_DDt, Lagrangian_DDt]", - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Re-extract this submesh from the adapted parent mesh.\n\nCalled automatically when the parent mesh adapts. Replaces the\nDM, rebuilds coordinates and vertex map, and reinitialises all\nMeshVariables on the new submesh (reset to zero).\n\nThe Python object is updated in-place \u2014 external references\nto this submesh remain valid.", "harvested_comments": [ - "# Parent class will set up default values and load u_Field into the solver", - "These are unique to the advection solver", - "self._E = self.mesh.vector.strain_tensor(self.u.sym)", - "## sets up DuDt and DFDt", - "# ._setup_history_terms()" + "Find which region label this submesh was extracted from", + "(stored at extraction time)", + "Extract new DM", + "Back up old variable data and coordinates for interpolation", + "old DOF coords" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1666, - "signature": "class _Parameters", - "parameters": [], + "name": "_build_dof_map", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2122, + "signature": "(self, parent_var, sub_var)", + "parameters": [ + { + "name": "parent_var", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "sub_var", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Build a DOF-level index mapping between parent and submesh variables.\n\nUses coordinate matching on DOF coordinates (exact match from\nDMPlexFilter shared nodes). Cached per variable pair.\n\nReturns (sub_rows, parent_rows) \u2014 numpy arrays of matching DOF indices.", + "harvested_comments": [ + "indices[matched] maps parent row \u2192 sub row" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1742, - "signature": "class _Parameters", + "name": "_update_projected_normals", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2467, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Project PETSc face normals (Gamma) onto a P1 field and normalise.\n\nCreates ``_projected_normals`` on first call, updates in-place\nthereafter. The result is a smooth, consistently-oriented unit\nnormal field that works well for penalty and Nitsche BCs on\ncurved boundaries.\n\nNOTE: this GLOBAL field point-evaluates ``mesh.Gamma`` whose petsc_n\nonly exists in surface-integral kernels, so it falls back to the\ncoordinate (radial for a circle) and does NOT track a deformed\nsurface. For deformation-aware, corner-correct normals use the\nper-boundary :meth:`boundary_normal` (which ``add_nitsche_bc`` now\nuses). This global field is retained unchanged for back-compat.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "__init__", + "name": "_assemble_boundary_normal", "kind": "method", - "file": "src/underworld3/systems/solver_template.py", - "line": 86, - "signature": "(self, mesh: uw.discretisation.Mesh, u_Field: Optional[uw.discretisation.MeshVariable] = None, degree: int = 2, verbose: bool = False, DuDt: Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]] = None, DFDt: Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]] = None)", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2537, + "signature": "(self, var, name)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "var", + "type_hint": null, "default": null, "description": "" }, { - "name": "u_Field", - "type_hint": "Optional[uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "2", - "description": "" - }, - { - "name": "verbose", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "DuDt", - "type_hint": "Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]]", - "default": "None", - "description": "" - }, - { - "name": "DFDt", - "type_hint": "Optional[Union[SemiLagrangian_DDt, Lagrangian_DDt, Eulerian_DDt]]", - "default": "None", + "name": "name", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Initialize the solver.\n\nParameters\n----------\nmesh : uw.discretisation.Mesh\n The computational mesh\nu_Field : uw.discretisation.MeshVariable, optional\n Solution field (created if None)\ndegree : int\n Polynomial degree for basis functions\nverbose : bool\n Enable verbose output\nDuDt : DDt object, optional\n Time derivative method for solution\nDFDt : DDt object, optional\n Time derivative method for flux", + "existing_docstring": "Fill ``var`` with the area-weighted outward facet normal assembled\nfrom the faces of boundary ``name`` only (see :meth:`boundary_normal`).", "harvested_comments": [ - "Track instances", - "Initialize parent class", - "Initialize default property values", - "Source term", - "Problem-specific parameters" + "faces carrying this boundary label: DM label named after the boundary,", + "stratum keyed by the boundary's value (same access the BC code uses).", + "NB: getStratumIS(value) for a value NOT in this rank's live value", + "set can hard-abort the interpreter (e.g. a rank holding no faces", + "of this boundary in parallel). Only query a live value." ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "SNES_MyEquation", + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 264, - "signature": "class _Parameters", + "name": "_cell_size_var", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2644, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", + "existing_docstring": "Lazily create / fetch the per-cell size MeshVariable (filled).\n\nMirrors the per-boundary normal machinery (:meth:`boundary_normal`):\na small ``reinit`` MeshVariable owned by the mesh, refreshed from the\ncurrent geometry. The reinit callback re-fills it during any remesh\ntransaction (deform / mover sweep), and :meth:`deform` / :meth:`adapt`\nre-fill it explicitly so BCs that captured ``cell_size()`` at setup\nread the new geometry at solve time.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 574, - "signature": "class _Parameters", - "parameters": [], + "name": "_assemble_cell_size", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2674, + "signature": "(self, var)", + "parameters": [ + { + "name": "var", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nNow uses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", - "harvested_comments": [], + "existing_docstring": "Fill ``var`` (degree-0 scalar) with each cell's characteristic size.\n\nUses the per-cell characteristic lengths ``self._radii`` computed by\n:meth:`_get_mesh_sizes` on the *current* geometry. A degree-0\ndiscontinuous variable's local DOFs and ``self._radii`` are BOTH\nindexed by this rank's cell-stratum order, so a direct assignment is\ncorrect on every rank.\n\nThis is deliberately a purely RANK-LOCAL operation (no ``var.coords``\naccess, no collective): mixing a rank-local fast path with a\ncollective fallback would diverge across ranks and deadlock, because\n``var.coords`` triggers the collective ``_get_coords_for_basis``.", + "harvested_comments": [ + "Empty partition (no local cells): nothing to fill on this rank.", + "Assign over the common length. In practice these match exactly (same", + "local cell set / ordering); the slice only guards a stray off-by-ghost", + "mismatch without ever taking a collective path on a subset of ranks." + ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 796, - "signature": "class _Parameters", - "parameters": [], + "name": "_resolve_slip_spec", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 2747, + "signature": "(self, slip_spec)", + "parameters": [ + { + "name": "slip_spec", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\n`sympy.oo` (infinity) for default values ensures that sympy.Min simplifies away\nthe conditionals when they are not required.\n\nUses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "existing_docstring": "Resolve a ``slip_spec`` to ``(slip_labels tuple, free_labels set)``.\n\nBack-compatible forms: ``True``/``\"all\"``/``\"ring\"``/``\"box\"`` \u2192 all\ngeometric boundary labels; ``False``/``None`` \u2192 none; a label name; a\nlist of labels; a ``dict {label: snap_bool}`` (``False`` = free\nsurface, slip but do not restore).", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1012, - "signature": "class _Parameters", + "name": "_assert_coord_mutation_allowed", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3063, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nUses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "existing_docstring": "Guard for :meth:`_deform_mesh`.\n\nMoving coordinates with the raw primitive skips the field /\nSL-DDt-history transfer. That is only safe (a) before the mesh\ncarries any variables/history (construction, restart-before-solvers)\nor (b) inside a sanctioned scope \u2014 a remesh transaction\n(``_in_remesh_transfer``) or a ``_coord_mutation()`` scope opened by\n:meth:`deform`, :meth:`ephemeral_coords`, or a trusted internal mover.\nOutside those, on a live mesh, raise with a pointer to the public API.", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1579, - "signature": "class _Parameters", + "name": "_coord_mutation", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3099, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nNow uses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", + "existing_docstring": "Internal: sanction direct ``_deform_mesh`` calls within this scope.\n\nRe-entrant. Does NOT itself transfer fields \u2014 callers either are the\ntransfer transaction (REMAP + ``on_remesh`` already run by\n``remesh_with_field_transfer``), restore saved state separately, or\nintend an ephemeral trial (see :meth:`ephemeral_coords`).", "harvested_comments": [], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 1850, - "signature": "class _Parameters", - "parameters": [], + "name": "_deform_mesh", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3189, + "signature": "(self, new_coords: numpy.ndarray, verbose = False, active_vars = None)", + "parameters": [ + { + "name": "new_coords", + "type_hint": "numpy.ndarray", + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "active_vars", + "type_hint": null, + "default": "None", + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nUses Parameter descriptor pattern for scalar permeability.\nMatrix-valued `s` remains instance-level (special case).", - "harvested_comments": [], + "existing_docstring": "This method will update the mesh coordinates and reset any cached coordinates in\nthe mesh and in equation systems that are registered on the mesh.\n\nThe coord array that is passed in should match the shape of self.data\n\n``active_vars`` (optional): restrict the per-variable DOF\ncoordinate-cache recomputation in\n:meth:`nuke_coords_and_rebuild` to this set of variables. The\ndefault ``None`` preserves today's behaviour \u2014 every registered\nvariable's coord cache is recomputed eagerly, which is the\nBUGFIX(#130) collective-safe path. Movers that opt in pass their\nown work-vars during the inner sweep (skipping non-mover-var\nrecompute n_outer\u00d7); the wrapper does a full recompute once at\nsweep exit by calling ``_deform_mesh`` again with\n``active_vars=None``.\n\n.. warning::\n\n This is the **internal** coordinate-mutation primitive. It moves\n nodes WITHOUT transferring fields or solver/DDt history onto the\n new layout. It may only be called inside a sanctioned coordinate-\n mutation scope (see :meth:`deform`, :meth:`ephemeral_coords`, and\n ``remesh_with_field_transfer``). A bare call on a mesh that already\n carries variables raises \u2014 use the public methods instead.", + "harvested_comments": [ + "130) collective-safe path. Movers that opt in pass their", + "Rebuild the _coords array view. nuke_coords_and_rebuild may", + "replace the coordinate vector internally (createCoordinateSpace),", + "leaving self._coords as a stale numpy view of the old buffer.", + "BUGFIX(#122): mark registered solvers for rebuild. Since PR #127" + ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models.py", - "line": 2009, - "signature": "class _Parameters", - "parameters": [], + "name": "_legacy_access", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3273, + "signature": "(self, *writeable_vars)", + "parameters": [ + { + "name": "*writeable_vars", + "type_hint": "'MeshVariable'", + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\nUses Parameter descriptor pattern for automatic lazy evaluation preservation\nwith unit-aware quantities.", - "harvested_comments": [], + "existing_docstring": "This context manager makes the underlying mesh variables data available to\nthe user. The data should be accessed via the variables `data` handle.\n\nAs default, all data is read-only. To enable writeable data, the user should\nspecify which variable they wish to modify.\n\nParameters\n----------\nwriteable_vars\n The variables for which data write access is required.\n\nExample\n-------\n>>> import underworld3 as uw\n>>> someMesh = uw.discretisation.FeMesh_Cartesian()\n>>> with someMesh._deform_mesh():\n... someMesh.data[0] = [0.1,0.1]\n>>> someMesh.data[0]\narray([ 0.1, 0.1])", + "harvested_comments": [ + "Invalidate DMInterpolation cache when DM structure changes", + "if already accessed within higher level context manager, continue.", + "set flag so variable status can be known elsewhere", + "add to de-access list to rewind this later", + "create & set vec" + ], "status": "partial", "needs": [ "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 100, - "signature": "class _Parameters", - "parameters": [], + "name": "_write_petsc_reload_variable", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3961, + "signature": "(self, viewer, var)", + "parameters": [ + { + "name": "viewer", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "var", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", + "existing_docstring": "Write one variable's PETSc DMPlex reload metadata to ``viewer``.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 284, - "signature": "class _Parameters", - "parameters": [], + "name": "_write_petsc_reload_file", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 3980, + "signature": "(self, checkpoint_file, variables, mode = 'w')", + "parameters": [ + { + "name": "checkpoint_file", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "variables", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": "'w'", + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", + "existing_docstring": "Write PETSc DMPlex section/vector reload metadata.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 392, - "signature": "class _Parameters", - "parameters": [], + "name": "_get_coords_for_var", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4417, + "signature": "(self, var)", + "parameters": [ + { + "name": "var", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.\n\n`sympy.oo` (infinity) for default values ensures that sympy.Min simplifies away\nthe conditionals when they are not required", - "harvested_comments": [], + "existing_docstring": "This function returns the vertex array for the\nprovided variable. If the array does not already exist,\nit is first created and then returned.", + "harvested_comments": [ + "if array already created, return." + ], "status": "partial", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 813, - "signature": "class _Parameters", - "parameters": [], + "name": "_get_coords_for_basis", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4432, + "signature": "(self, degree, continuous)", + "parameters": [ + { + "name": "degree", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "continuous", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", - "harvested_comments": [], + "existing_docstring": "This function returns the vertex array for the\nprovided variable. If the array does not already exist,\nit is first created and then returned.", + "harvested_comments": [ + "Clean up the PETSc interpolation objects built above. Without this", + "they accumulate until Python GC runs \u2014 noticeable in long adapt", + "loops that re-fill the coord cache per variable." + ], "status": "partial", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1489, - "signature": "class _Parameters", + "name": "_mark_faces_inside_and_out", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4653, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", - "harvested_comments": [], + "existing_docstring": "Create a collection of control point pairs that are slightly inside\nand slightly outside each mesh face (mirrors to each other). This\nallows a fast lookup of whether we on the inside or outside of the plane\ndefined by a face (i.e. same side or other side as the cell centroid). If we are inside\nfor all faces in a convex polyhedron, then we are inside the cell.\n\nInternal Coordinate System Access Pattern\n------------------------------------------\nThis method uses `self._coords` (raw PETSc array) instead of `self.data`\nor `self.X.coords` (unit-wrapped properties) for performance and correctness:\n\n1. **Guard at boundaries**: External interfaces use unit-aware properties\n2. **Raw access internally**: Internal geometric calculations use `self._coords`\n3. **Performance**: Avoids UnitAwareArray overhead in tight loops\n4. **Correctness**: Prevents unit conversion issues in geometric operations\n\nThis is the recommended pattern for internal mesh operations that manipulate\ncoordinates directly.", + "harvested_comments": [ + "Build face control points from the nav DM (includes ghost", + "cells on manifold meshes). Volume meshes have _nav_dm is", + "None and we use self.dm directly.", + "All elements in our mesh are a single type", + "Use raw internal array for internal mesh operations (avoid unit-aware wrapping)" + ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_Parameters", - "kind": "class", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1574, - "signature": "class _Parameters", + "name": "_get_owned_cells_mask", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4771, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Any material properties that are defined by a constitutive relationship are\ncollected in the parameters which can then be defined/accessed by name in\nindividual instances of the class.", - "harvested_comments": [], + "existing_docstring": "Return a boolean array of length n_local_cells (NAV-DM\ncells) where True means the cell is owned by this rank, False\nmeans it's a ghost cell brought in by\n``DMPlexDistributeOverlap``.\n\nOn a non-overlapped DM (the default for volume meshes), every\ncell is owned and the mask is all True \u2014 the downstream filter\nis a no-op. On the nav DM of a manifold mesh, ghost cells\nappear as leaves of the point SF in the cell range\n``[cStart, cEnd)``.\n\nCached on the mesh; rebuilt only when ``_mesh_version`` changes.", + "harvested_comments": [ + "Leaves are global point IDs; cells live in [cStart, cEnd)." + ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_object_viewer", + "name": "_test_if_points_in_cells_internal", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 133, - "signature": "def _object_viewer(self):", - "parameters": [], + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4813, + "signature": "(self, points, cells, on_boundary = True, tol = 0.0)", + "parameters": [ + { + "name": "points", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cells", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "on_boundary", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "tol", + "type_hint": null, + "default": "0.0", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Determine if the given points lie in the suggested cells.\nUses a mesh skeletonization array to determine whether the point is\nwith the convex polygon / polyhedron defined by a cell.\n\nExact if applied to a linear mesh, approximate otherwise.\n\nOn an overlapped DM (manifold meshes), a query point may land in\na *ghost* cell \u2014 a cell owned by another rank and present locally\nonly as part of the partition halo. Ghost cells are explicitly\nrejected so a single rank claims each point cleanly; the migrate\nloop's iterative fallback then routes the rejected point to the\nactual owning rank (where the same cell is genuinely owned).\n\nParameters\n----------\npoints : numpy.ndarray\n Coordinate array, assumed already in model units (this internal\n helper does not perform unit conversion \u2014 use the public\n `test_if_points_in_cells` for unit-aware input).\ncells : numpy.ndarray\n 1-D cell indices to test, one per point.\non_boundary : bool, default True\n If True (the default), a point exactly on a cell face counts as\n inside that cell \u2014 the natural semantics for FE evaluation,\n where the basis at a shared face/vertex is consistent across\n the adjacent cells. A query point lying on a face shared by N\n cells passes the test for any of those N cells.\n\n If False, a point exactly on a face is reported as NOT inside \u2014\n strict-inside semantics. Use this when uniqueness matters (a\n strict-ownership scheme where a shared-face point being claimed\n by all adjacent cells would be a bug).\n\n The implementation compares the squared distance from the query\n to a mirrored inner/outer control-point pair placed \u00b11e-3 along\n the face normal; a point exactly on the face has zero distance\n difference. With on_boundary=True the test accepts diff >= -1e-12\n (well below the 1e-3 control-point offset, well above 64-bit\n float roundoff); with on_boundary=False the test requires diff > 0.\ntol : float, default 0.0\n Face-RELATIVE tolerance \u2014 overrides ``on_boundary``'s absolute\n -1e-12 floor when nonzero. The test becomes\n ``diff > -tol * |O - I|^2`` (i.e. relative to the\n control-point separation squared). With ``tol == 0`` (the\n default) ``on_boundary`` controls the test as documented above.\n With ``tol > 0`` the test is relaxed to admit points within\n roughly ``tol`` of the face (relative to the face-normal\n separation), while still rejecting points that lie inside a\n *different* cell (whose diff is strongly negative). The\n parallel evaluation locator\n (``Mesh._robust_owning_cells``) uses ``tol=1e-2`` to admit\n on-face / partition-seam node queries that ``on_boundary=True``'s\n absolute 1e-12 floor is too tight to accept \u2014 for query\n coordinates slightly off the face (e.g. RBF-shifted node\n points), the geometric scale of the test needs to match the\n *mesh* spacing, not float roundoff. See memory\n project_pr207_loose_boundary_clash.", + "harvested_comments": [ + "Internal version - points assumed to already be in model units", + "Face-relative tolerance branch (parallel evaluation locator).", + "Takes precedence over on_boundary: a non-zero tol expresses", + "a geometric tolerance on the face-normal separation\u00b2; the", + "absolute on_boundary floor (~1e-12) is far below it and the" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_reset", + "name": "_mark_local_boundary_faces_inside_and_out", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 145, - "signature": "def _reset(self):", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 4928, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Create a collection of control point pairs that are slightly inside\nand slightly outside each boundary-defining face (mirrors to each other). This\nallows a fast lookup of whether we on the inside or outside of the domain.\nWe cannot ensure convexity, so this is approximate when close to the boundary", + "harvested_comments": [ + "Build boundary control points from the nav DM (sees the", + "ghost cells on manifold meshes). On volume meshes nav_dm is", + "On an overlapped DM (manifold meshes with the partition halo),", + "the outer edge of the halo masquerades as a boundary: faces", + "there have ``getJoin(face).shape[0] == 1`` because only one" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_build", + "name": "_get_closest_local_cells_internal", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 357, - "signature": "def _build(self,\n verbose: bool = False,\n debug: bool = False,\n debug_name: str = None,\n ):", - "parameters": [], - "returns": null, - "existing_docstring": null, + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5209, + "signature": "(self, coords: numpy.ndarray, on_boundary: bool = True, tol: float = 0.0) -> numpy.ndarray", + "parameters": [ + { + "name": "coords", + "type_hint": "numpy.ndarray", + "default": null, + "description": "" + }, + { + "name": "on_boundary", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "tol", + "type_hint": "float", + "default": "0.0", + "description": "" + } + ], + "returns": "numpy.ndarray", + "existing_docstring": "This method uses a kd-tree algorithm to find the closest\ncells to the provided coords. For a regular mesh, this should\nbe exactly the owning cell, but if the mesh is deformed, this\nis not guaranteed. Also compares the distance from the cell to the\npoint - if this is larger than the \"cell size\" then returns -1\n\n``on_boundary`` and ``tol`` are forwarded to the in-cell\ncontainment test (see ``_test_if_points_in_cells_internal``).\nDefault ``(on_boundary=True, tol=0.0)`` admits on-face queries\nat PR #207's absolute -1e-12 floor \u2014 the FE-evaluation-natural\nsemantics. Pass ``on_boundary=False`` for strict-inside (a\npoint exactly on a face returns -1). ``tol > 0`` admits on-face\npoints at an absolute -1e-12 floor (matches PR #207). ``tol > 0``\nadmits on-face points at a face-relative tolerance, taking\nprecedence over ``on_boundary``.\n\nParameters:\n-----------\ncoords:\n An array of the coordinates for which we wish to determine the\n closest cells. This should be a 2-dimensional array of\n shape (n_coords,dim), assumed already in model units (this\n internal helper does not perform unit conversion \u2014 use the\n public `get_closest_local_cells` for unit-aware input).\non_boundary : bool, default True\n Forwarded to `_test_if_points_in_cells_internal`. If True (the\n default), queries exactly on a cell face count as inside that\n cell \u2014 the natural semantics for FE-evaluation hints (every mesh\n vertex sits on the faces of every cell containing it). If False,\n strict-inside semantics; boundary queries come back as -1.\ntol : float, default 0.0\n Face-relative tolerance, forwarded to\n `_test_if_points_in_cells_internal`. When > 0, the in-cell\n test becomes ``diff > -tol * |O-I|\u00b2`` and takes precedence\n over ``on_boundary``. Used by the parallel evaluation\n locator (`Mesh._robust_owning_cells`) to admit on-face /\n partition-seam node queries at a mesh-spacing-relative\n tolerance \u2014 wider than ``on_boundary=True``'s absolute\n -1e-12 floor.\n\nReturns:\n--------\nclosest_cells:\n An array of indices representing the cells closest to the provided\n coordinates. This will be a 1-dimensional array of\n shape (n_coords).", + "harvested_comments": [ + "207's absolute -1e-12 floor \u2014 the FE-evaluation-natural", + "207). ``tol > 0``", + "Internal version - coords assumed to already be in model units", + "Create index if required", + "We need to filter points that lie outside the mesh but" + ], + "status": "complete", + "needs": [], + "parent_class": "Mesh", + "is_public": false + }, + { + "name": "_robust_owning_cells", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5446, + "signature": "(self, coords: numpy.ndarray) -> numpy.ndarray", + "parameters": [ + { + "name": "coords", + "type_hint": "numpy.ndarray", + "default": null, + "description": "" + } + ], + "returns": "numpy.ndarray", + "existing_docstring": "Per-point owning cell for parallel evaluation (coords in model units).\n\nThis is the strict barycentric/cell-wall locator\n(:meth:`_get_closest_local_cells_internal`) with a *tight* face\ntolerance (``_EVAL_FACE_TOL``): it returns the containing cell for\ninterior points and a valid sharing cell for genuinely-on-face points,\nand ``-1`` for points that lie inside a *different* cell or outside the\nlocal mesh. Crucially it does **not** fall back to a bounding-sphere\n\"nearest cell\" \u2014 that earlier fallback let a rank claim a point that\nanother rank actually owns, so the eval-swarm migration stranded it on\nthe wrong rank and \u03be-clamp-evaluated it in an adjacent cell (the\npartition-seam hotspots). With the strict+tol locator the point is\nfound only by its true owner, the migration delivers it there, and it\nevaluates exactly.\n\nNever calls PETSc ``DMLocatePoints`` (slow, raises out-of-domain), and\nis purely kd-tree / Euclidean \u2014 manifold-safe, no manifold branch.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_setup_problem_description", + "name": "_eval_use_robust_location", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 401, - "signature": "def _setup_problem_description(self):", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5473, + "signature": "(self) -> bool", "parameters": [], - "returns": null, - "existing_docstring": null, + "returns": "bool", + "existing_docstring": "Single switch for the parallel evaluation cell-location strategy.\n\nReturns True when ``uw.function`` evaluation should locate cells with\nthe bulletproof barycentric hint (:meth:`_robust_owning_cells`) and the\n``DMLocatePoints`` bypass (``petsc_tools.c``), rather than PETSc's\n``DMLocatePoints``. This is the *one place* the policy lives; the\nevaluate_nd classifier, the petsc_interpolate hint, and the\nDMInterpolation wrapper all defer to it so the three stay consistent.\n\nTwo conditions, both required:\n\n* **parallel only** (``uw.mpi.size > 1``) \u2014 in serial the on-face/edge\n node points go to RBF-at-node (exact) and PETSc/the cell-wall test\n are reliable with a single domain, so serial keeps the validated path\n bit-for-bit. The parallel-only failure is the rank-local RBF / wrong-\n region value at partition-seam node points.\n* **hint is authoritative** \u2014 simplex cells (``dm.isSimplex()``: planar\n faces, affine reference map, exact face containment) or manifold\n meshes (``dim != cdim``: PETSc's in-cell test is unreliable near\n 2-manifold simplex edges in 3-D). On non-simplex volume meshes\n (quads/hexes) deformed faces can be non-planar and the kdtree-nearest\n cell can be wrong, so those keep PETSc's DMLocatePoints search.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_get_dof_partition_by_field_id", + "name": "_get_mesh_sizes", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 715, - "signature": "def _get_dof_partition_by_field_id(self,\n section_type: str,\n field_id: int,\n filename: Optional[str | None] = None,\n outputPath: Optional[str] = \"\"):", - "parameters": [], + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5501, + "signature": "(self, verbose = False)", + "parameters": [ + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Obtain the (local) mesh radii and centroids using kdtree distances\nThis routine is called when the mesh is built / rebuilt", + "harvested_comments": [ + "Use raw internal array for internal mesh operations (avoid unit-aware wrapping)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_setup_discretisation", + "name": "_get_mesh_centroids", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 919, - "signature": "def _setup_discretisation(self, verbose=False):", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5535, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Obtain and cache the (local) mesh centroids using underworld swarm technology.\nThis routine is called when the mesh is built / rebuilt\n\nThe global cell number corresponding to a centroid is (supposed to be)\nself.dm.getCellNumbering().array.min() + index", + "harvested_comments": [ + ") = petsc_discretisation.petsc_fvm_get_local_cell_sizes(self)" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_setup_pointwise_functions", + "name": "_increment_mesh_version", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1070, - "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 5733, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Manually increment mesh version to notify swarms of coordinate changes.\nThis is called automatically when mesh.points is modified, but can be\ncalled manually if coordinates are changed through other means.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "Mesh", "is_public": false }, { - "name": "_setup_solver", - "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1206, - "signature": "def _setup_solver(self, verbose=False):", - "parameters": [], + "name": "_write_compat_groups", + "kind": "function", + "file": "src/underworld3/discretisation/discretisation_mesh.py", + "line": 6181, + "signature": "(mesh, var, var_h5_path)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "var", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "var_h5_path", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Write ``/vertex_fields/`` or ``/cell_fields/`` compatibility groups.\n\nUses ``uw.function.write_vertices_to_viewer`` (PETSc interpolation +\nViewerHDF5) for continuous variables, and\n``uw.function.write_cell_field_to_viewer`` for cell/DG-0 variables.\nPETSc handles all parallel I/O natively.\n\nVertex coordinates are also written to ``/vertex_fields/coordinates``\nfor XDMF compatibility.\n\nParameters\n----------\nmesh : Mesh\n The parent mesh.\nvar : MeshVariable\n The variable whose data has already been written to *var_h5_path*\n by ``var.write()`` (so ``var._gvec`` is up-to-date).\nvar_h5_path : str\n Path to the HDF5 file (already contains ``/fields/``).", + "harvested_comments": [ + "Some PETSc versions (3.21+) write /vertex_fields/ or /cell_fields/", + "automatically during var.write(). Remove any pre-existing group so", + "that our compat writer can create it afresh (otherwise PETSc error 76", + "on duplicate dataset)." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_object_viewer", + "name": "__new__", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1347, - "signature": "def _object_viewer(self):", - "parameters": [], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 102, + "signature": "(cls, varname: Union[str, list], mesh: 'Mesh', num_components: Union[int, tuple] = None, vtype: Optional['uw.VarType'] = None, degree: int = 1, continuous: bool = True, varsymbol: Union[str, list] = None, _register: bool = True, units: Optional[str] = None, units_backend: Optional[str] = None, remesh_policy = None)", + "parameters": [ + { + "name": "varname", + "type_hint": "Union[str, list]", + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": "'Mesh'", + "default": null, + "description": "" + }, + { + "name": "num_components", + "type_hint": "Union[int, tuple]", + "default": "None", + "description": "" + }, + { + "name": "vtype", + "type_hint": "Optional['uw.VarType']", + "default": "None", + "description": "" + }, + { + "name": "degree", + "type_hint": "int", + "default": "1", + "description": "" + }, + { + "name": "continuous", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "varsymbol", + "type_hint": "Union[str, list]", + "default": "None", + "description": "" + }, + { + "name": "_register", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "units", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "units_backend", + "type_hint": "Optional[str]", + "default": "None", + "description": "" + }, + { + "name": "remesh_policy", + "type_hint": null, + "default": "None", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Create or return existing MeshVariable instance.\n\nHandles object uniqueness and mesh DM state management.", + "harvested_comments": [ + "# Check if already defined (return existing object)", + "NOTE: DM reconstruction is now handled in _setup_ds() - no snapshotting needed here", + "Create new instance", + "Store parameters for __init__" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_setup_pointwise_functions", + "name": "_create_variable_array", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1674, - "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", - "parameters": [], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 530, + "signature": "(self, initial_data = None)", + "parameters": [ + { + "name": "initial_data", + "type_hint": null, + "default": "None", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Factory function to create NDArray_With_Callback for variable data.\nFollows the same pattern as mesh.points implementation.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Array object with callback for automatic PETSc synchronization", + "harvested_comments": [ + "Create NDArray_With_Callback (following mesh._points pattern)", + "Allow operations like existing arrays", + "Single callback function (following mesh_update_callback pattern)", + "This guard handles cases where the array is accessed during", + "object teardown (e.g. at application exit or mesh rebuilds)," ], - "parent_class": null, + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_setup_solver", + "name": "_create_flat_data_array", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1819, - "signature": "def _setup_solver(self, verbose=False):", - "parameters": [], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 601, + "signature": "(self, initial_data = None)", + "parameters": [ + { + "name": "initial_data", + "type_hint": null, + "default": "None", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Factory function to create NDArray_With_Callback for backward-compatible flat data.\nReturns data in shape (-1, num_components) using pack_raw/unpack_raw methods.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Array object with callback for automatic PETSc synchronization", + "harvested_comments": [ + "Use unpack_raw to get flat format (-1, num_components)", + "Create NDArray_With_Callback for flat data", + "Allow operations like existing arrays", + "Callback for flat data format", + "Only act on data-changing operations" ], - "parent_class": null, + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", "is_public": false }, { "name": "_object_viewer", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2047, - "signature": "def _object_viewer(self):", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 667, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "This will substitute specific information about this object", + "harvested_comments": [ + "feedback on this instance" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_setup_history_terms", + "name": "_get_kdtree", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2300, - "signature": "def _setup_history_terms(self):", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 992, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Return a cached KDTree for this variable's DOF locations.\nRebuilds automatically if the parent mesh has deformed.", + "harvested_comments": [ + "Use non-dimensional coordinates for internal caching (avoids UnitAwareArray overhead)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_object_viewer", + "name": "_replace_from_adapted_mesh", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2447, - "signature": "def _object_viewer(self):", - "parameters": [], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1808, + "signature": "(self, temp_var, adapted_mesh)", + "parameters": [ + { + "name": "temp_var", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "adapted_mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Replace internal storage after mesh adaptation.\n\nCalled by mesh.adapt() to update this variable's internal PETSc\nstructures after the mesh's discretization has changed. The data\nhas already been interpolated to temp_var; this method copies that\ndata into this variable's updated storage.\n\nParameters\n----------\ntemp_var : MeshVariable\n A temporary variable on the adapted mesh containing the\n interpolated data for this variable.\nadapted_mesh : Mesh\n The mesh object (same object, but with updated internal DM).\n\nNotes\n-----\nThis is an internal method called by mesh.adapt(). Users should\nnot need to call this directly.\n\nAfter this method returns, all user references to this variable\nremain valid and contain the interpolated data on the adapted mesh.", + "harvested_comments": [ + "Destroy old PETSc vectors", + "The mesh reference is still valid (same object, updated internals)", + "But we need to find/create our field on the new DM", + "Check if our field exists on the new DM", + "Need to add field to new DM (this happens if adapt() didn't pre-create variables)" ], - "parent_class": null, + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_setup_pointwise_functions", + "name": "_sync_lvec_to_gvec", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2562, - "signature": "def _setup_pointwise_functions(self, verbose=False, debug=False, debug_name=None):", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 1927, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Ensure the global vector reflects the current local vector.\n\nAfter PETSc solves (Stokes, Projection, etc.), data lives only in\n``_lvec``. Any operation that reads ``_gvec`` (writes, norms, etc.)\nmust call this first. The scatter uses the variable's sub-DM so it\nworks for any field order (P1, P2, DG-0, \u2026).\n\nThe operation is idempotent \u2014 calling it when ``_gvec`` is already\nup-to-date is harmless.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_setup_solver", + "name": "_create_array_view", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2934, - "signature": "def _setup_solver(self, verbose=False):", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2014, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Create array view of canonical data using appropriate conversion strategy.\n\nStrategy depends on variable complexity:\n- Scalars/Vectors: Simple reshape operations\n- 2D+ Tensors: Complex pack/unpack operations\n\nReturns\n-------\nArrayView\n Array-like object that delegates changes back to canonical data", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_BaseMeshVariable", - "kind": "class", + "name": "_is_simple_variable", + "kind": "method", "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 73, - "signature": "class _BaseMeshVariable", + "line": 2032, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "The MeshVariable class generates a variable supported by a finite element mesh and the\nunderlying sympy representation that makes it possible to construct expressions that\ndepend on the values of the MeshVariable.\n\nTo set / read nodal values, use the numpy interface via the 'data' property.\n\nParameters\n----------\nvarname :\n A text name for this variable. Use an R-string if a latex-expression is used\nmesh :\n The supporting underworld mesh.\nnum_components :\n The number of components this variable has.\n For example, scalars will have `num_components=1`,\n while a 2d vector would have `num_components=2`.\nvtype :\n Optional. The underworld variable type for this variable.\n If not defined it will be inferred from `num_components`\n if possible.\ndegree :\n The polynomial degree for this variable.\nvarsymbol:\n Over-ride the varname with a symbolic form for printing etc (latex). Should be an R-string.", + "existing_docstring": "Check if this is a simple scalar/vector variable (not a complex tensor)", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_object_viewer", + "name": "_create_simple_array_view", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 1804, + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2036, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Array view for scalars/vectors using simple reshape operations", "harvested_comments": [ - "# feedback on this instance" + "Simple reshape: (-1, num_components) -> (N, a, b)", + "For simple variables, reshape to (N, a, b) format", + "Apply dimensionalization if needed", + "Check if variable has units and model has reference quantities", + "Variable has units - wrap with UnitAwareArray" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_AdvectionDiffusion", + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_object_viewer", + "name": "_create_tensor_array_view", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2307, + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2372, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Array view for complex tensors using pack/unpack operations", "harvested_comments": [ - "# feedback on this instance" + "Use complex pack/unpack for tensor layouts", + "Apply dimensionalization if needed", + "Check if variable has units and model has reference quantities", + "Variable has units - wrap with UnitAwareArray", + "Get variable units (needed for both branches)" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_Diffusion", + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_object_viewer", + "name": "_create_canonical_data_array", "kind": "method", - "file": "src/underworld3/systems/solvers.py", - "line": 2712, + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2747, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Create the single canonical data array with PETSc synchronization for MeshVariable.\nThis is the ONLY method that creates arrays with PETSc callbacks.\n\nHandles mesh-specific requirements like locking and ghost value synchronization.\n\nReturns\n-------\nNDArray_With_Callback\n Canonical array object with callback for automatic PETSc synchronization", "harvested_comments": [ - "# feedback on this instance" + "Ensure PETSc vector is available", + "Get direct access to PETSc vector in packed format", + "Create NDArray_With_Callback with proper shape and data", + "Single canonical callback for PETSc synchronization", + "Only act on data-changing operations" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SNES_NavierStokes", + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "_PintHelper", - "kind": "class", - "file": "src/underworld3/units.py", - "line": 49, - "signature": "class _PintHelper", + "name": "_dimensionalise_stat", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 2827, + "signature": "(self, value: Union[float, tuple]) -> Union[float, tuple]", + "parameters": [ + { + "name": "value", + "type_hint": "Union[float, tuple]", + "default": null, + "description": "" + } + ], + "returns": "Union[float, tuple]", + "existing_docstring": "Helper to dimensionalise statistical values using uw.dimensionalise().\n\nTakes non-dimensional value(s) from PETSc and converts to dimensional\nform using the variable's units and model reference quantities.\n\nParameters\n----------\nvalue : float or tuple\n Non-dimensional value(s) from PETSc\n\nReturns\n-------\nfloat, tuple, or UWQuantity\n Dimensionalised value(s) if units are enabled, else unchanged", + "harvested_comments": [ + "Check if units mode is enabled", + "Backward compatible - no units mode or variable has no units", + "Extract dimensionality from units", + "self.units is already a Pint Unit object with .dimensionality attribute", + "Pint Unit object - extract dimensionality directly" + ], + "status": "complete", + "needs": [], + "parent_class": "_BaseMeshVariable", + "is_public": false + }, + { + "name": "_scalar_stats", + "kind": "method", + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3135, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Simple helper for Pint operations.\n\nThis replaces the deprecated PintBackend class with direct Pint usage.\nProvides a minimal interface for the few places that need backend-like operations.", - "harvested_comments": [], - "status": "partial", + "existing_docstring": "Statistics for scalar variables (original implementation).", + "harvested_comments": [ + "Now returns value directly, not tuple", + "Now returns value directly, not tuple" + ], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_vector_stats", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 270, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3159, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Statistics for vector variables using magnitude.", + "harvested_comments": [ + "Create temporary scalar variable for magnitude", + "Compute magnitude: |v| = sqrt(v\u00b7v)", + "Get scalar stats on magnitude", + "Update with vector-specific info", + "Cleanup temporary variable" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_tensor_stats", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 559, - "signature": "(self, unknowns, material_name: str = None)", - "parameters": [ - { - "name": "unknowns", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "material_name", - "type_hint": "str", - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", + "line": 3197, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Statistics for tensor variables using Frobenius norm.", "harvested_comments": [ - "All this needs to do is define the", - "viscosity property and init the parent(s)", - "In this case, nothing seems to be needed.", - "The viscosity is completely defined", - "in terms of the Parameters" + "Create temporary scalar variable for Frobenius norm", + "Compute Frobenius norm: ||A||_F = sqrt(sum(A_ij^2))", + "Get scalar stats on Frobenius norm", + "Update with tensor-specific info", + "Cleanup temporary variable" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscousFlowModel", + "parent_class": "_BaseMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "__new__", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 597, - "signature": "(inner_self, _owning_model)", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 66, + "signature": "(cls, varname, mesh, *args, **kwargs)", "parameters": [ { - "name": "inner_self", + "name": "varname", "type_hint": null, "default": null, "description": "" }, { - "name": "_owning_model", + "name": "mesh", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "_Parameters", - "is_public": false - }, - { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 776, - "signature": "(self, unknowns, material_name: str = None)", - "parameters": [ + }, { - "name": "unknowns", + "name": "*args", "type_hint": null, "default": null, "description": "" }, { - "name": "material_name", - "type_hint": "str", - "default": "None", + "name": "**kwargs", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Custom __new__ to ensure proper initialization and registration.", "harvested_comments": [ - "All this needs to do is define the", - "non-paramter properties that we want to", - "use in other expressions and init the parent(s)" + "Create the instance", + "Perform early registration to override any base variable registration", + "Register the wrapper immediately (this will overwrite any base variable registration)", + "Store reference for later use in __init__" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoPlasticFlowModel", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_setup_registration", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 846, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 197, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Register with default model, replacing any existing registration from base variable.", + "harvested_comments": [ + "All variables register with default model", + "Force registration of wrapper, even if base variable registered itself", + "Use either the early name (from __new__) or the processed name (from base variable)", + "This will overwrite any existing registration", + "Auto-derive scaling coefficient if model has reference quantities" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_auto_derive_scaling_coefficient", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 967, - "signature": "(self, unknowns, order = 1, material_name: str = None)", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 213, + "signature": "(self, model)", "parameters": [ { - "name": "unknowns", + "name": "model", "type_hint": null, "default": null, "description": "" - }, - { - "name": "order", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "material_name", - "type_hint": "str", - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Automatically derive scaling coefficient from model's reference quantities.", "harvested_comments": [ - "# We just need to add the expressions for the stress history terms in here.\\", - "# They are properties to hold expressions that are persistent for this instance", - "# (i.e. we only update the value, not the object)", - "Store material_name before creating expressions (needed by create_unique_symbol)", - "This may not be defined at initialisation time, set to None until used" + "Silently skip if module not available or model incomplete" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_setup_persistence_features", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1074, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 229, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Setup additional persistence capabilities for persistent variables.", "harvested_comments": [ - "Internal symbols for stress history (not parameters, internal state)", - "# The following expressions are containers for derived/computed values.", - "# They have @property calls to retrieve / calculate them.", - "# We keep them as expression containers for lazy evaluation." + "Store qualified name for later reference (for persistent variables)" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1602, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } + "name": "_remesh_managed_by", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 382, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Operator that owns this variable's transfer on a remesh.\n\n``None`` means the generic per-variable pass in\n:func:`~underworld3.discretisation.remesh.remesh_with_field_transfer`\nhandles it (the common case). Set to an operator (e.g. a\n``SemiLagrangian`` DDt) when that operator's ``on_remesh`` hook\nwill transfer this var coherently with its history.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "EnhancedMeshVariable", + "is_public": false + }, + { + "name": "_lvec", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 410, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Local PETSc vector.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1667, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "name": "_gvec", + "kind": "property", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 415, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Set default diffusivity as an identity matrix wrapped in an expression", - "Store the validated diffusivity as a diagonal matrix" - ], - "status": "none", + "existing_docstring": "Global PETSc vector.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_sync_lvec_to_gvec", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1743, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 419, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Synchronize local PETSc vector to global PETSc vector.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_set_vec", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1872, - "signature": "(inner_self, _owning_model, permeabililty: Union[float, sympy.Function] = 1)", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 434, + "signature": "(self, available = True)", "parameters": [ { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", + "name": "available", "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "permeabililty", - "type_hint": "Union[float, sympy.Function]", - "default": "1", + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Note: typo in param name preserved for compatibility", - "Row matrix (1, dim) to match grad_u from jacobian" - ], - "status": "none", + "existing_docstring": "Initialize PETSc vector.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "_get_kdtree", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1994, - "signature": "(self, unknowns, material_name: str = None)", - "parameters": [ - { - "name": "unknowns", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "material_name", - "type_hint": "str", - "default": "None", - "description": "" - } + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 591, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "KDTree access.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "EnhancedMeshVariable", + "is_public": false + }, + { + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 684, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Enhanced representation showing persistence and units info.", "harvested_comments": [ - "All this needs to do is define the", - "viscosity property and init the parent(s)", - "In this case, nothing seems to be needed.", - "The viscosity is completely defined", - "in terms of the Parameters" + "Add persistence info", + "Add units info" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "TransverseIsotropicFlowModel", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", + "name": "__str__", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2042, - "signature": "(inner_self, _owning_model)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "_owning_model", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/discretisation/enhanced_variables.py", + "line": 698, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "String representation.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "EnhancedMeshVariable", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 66, - "signature": "(self, dim: int, u_dim: int)", + "name": "_gather_transfer_vars", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 132, + "signature": "(mesh: 'Mesh') -> dict", "parameters": [ { - "name": "dim", - "type_hint": "int", - "default": null, - "description": "" - }, - { - "name": "u_dim", - "type_hint": "int", + "name": "mesh", + "type_hint": "'Mesh'", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": null, + "returns": "dict", + "existing_docstring": "Snapshot registered mesh variables, partitioned by policy.\n\nReturns\n``{\"remap\": [vars], \"carry\": [vars], \"reinit\": [vars], \"managed\": [vars]}``.\nThe first three are vars the generic per-variable pass handles\ndirectly; \"managed\" collects vars whose ``_remesh_managed_by`` is\nset (an operator's ``on_remesh`` hook owns their transfer). Managed\nvars are excluded from the first three buckets even if their policy\nis REMAP/CARRY/REINIT \u2014 the operator is the single source of truth.", "harvested_comments": [ - "Define / identify the various properties in the class but leave", - "the implementation to child classes. The constitutive tensor is", - "defined as a template here, but should be instantiated via class", - "properties as required.", - "We provide a function that converts gradients / gradient history terms" + "Operator-managed vars are deferred to the registered hook so the", + "generic pass does not also transfer them. We still snapshot", + "their data (see _remesh_with_field_transfer_impl) so a hook", + "that needs to fall back to REMAP (e.g. on an OT reset adapt)", + "has the original values to work from." ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 106, - "signature": "(inner_self, k = 1)", + "name": "_snapshot_remap_data", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 173, + "signature": "(remap_vars)", "parameters": [ { - "name": "inner_self", + "name": "remap_vars", "type_hint": null, "default": null, "description": "" - }, - { - "name": "k", - "type_hint": null, - "default": "1", - "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Copy the current ``.data`` array of every REMAP var (defensive copy).", + "harvested_comments": [ + "If a var's data is not addressable yet (lazy alloc, etc.),", + "skip it \u2014 nothing to snapshot. The transfer pass will just", + "not touch it." + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 290, - "signature": "(inner_self, viscosity: Union[float, sympy.Function] = None)", + "name": "_remap_one_var", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 190, + "signature": "(var, old_X, new_X, mesh)", "parameters": [ { - "name": "inner_self", + "name": "var", "type_hint": null, "default": null, "description": "" }, { - "name": "viscosity", - "type_hint": "Union[float, sympy.Function]", - "default": "None", + "name": "old_X", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "new_X", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mesh", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Helper kept separate for diagnostics; not currently used.\n\nThe actual transfer is done in ``remesh_with_field_transfer`` by\nmoving the mesh once between the two coordinate states and calling\n``global_evaluate`` per var, which is cheaper than per-var deform.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 308, - "signature": "(self, dim)", + "name": "_new_coord_cache", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 200, + "signature": "(mesh, remap_vars)", "parameters": [ { - "name": "dim", + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "remap_vars", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Capture each REMAP var's DOF coordinates ON THE NEW MESH.\n\nCalled *after* the mover has produced ``new_X`` \u2014 the mesh is at\n``new_X``, so ``var.coords`` returns the correct DOF positions for\nthat variable's (degree, continuous) basis. Returned dict is the\ntarget-point set for ``global_evaluate`` once the mesh is deformed\nback to the old state.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscousFlowModel", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 387, - "signature": "(self, dim)", + "name": "_remesh_with_field_transfer_impl", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 290, + "signature": "(mesh, do_move) -> bool", "parameters": [ { - "name": "dim", + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "do_move", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "returns": "bool", + "existing_docstring": "Body of :func:`remesh_with_field_transfer`. Split so the\nre-entrancy guard wraps a single try/finally.", + "harvested_comments": [ + "Snapshot managed vars too \u2014 needed when a hook opts out of ALE", + "for this adapt and falls back to REMAP (see RemeshContext docs).", + "Run the mover. It is allowed to call _deform_mesh many times; .data", + "is untouched by _deform_mesh, so REMAP snapshots stay valid.", + "Mover short-circuited (skip threshold, no metric change, ...)." + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoPlasticFlowModel", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 401, - "signature": "(inner_self, materialIndex: Union[uw.swarm.SwarmVariable, uw.discretisation.MeshVariable] = None, shear_viscosity_0: Union[list, sympy.Function] = [1], shear_viscosity_min: Union[list, sympy.Function] = [-sympy.oo], shear_viscosity_max: Union[list, sympy.Function] = [sympy.oo], yield_stress: Union[list, sympy.Function] = [sympy.oo], yield_stress_min: Union[list, sympy.Function] = [-sympy.oo], strainrate_inv_II: sympy.Function = sympy.oo, strainrate_inv_II_min: float = 0.0, averaging_method: str = 'HA')", + "name": "_iter_active_hooks", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 358, + "signature": "(mesh)", "parameters": [ { - "name": "inner_self", + "name": "mesh", "type_hint": null, "default": null, "description": "" - }, - { - "name": "materialIndex", - "type_hint": "Union[uw.swarm.SwarmVariable, uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "shear_viscosity_0", - "type_hint": "Union[list, sympy.Function]", - "default": "[1]", - "description": "" - }, - { - "name": "shear_viscosity_min", - "type_hint": "Union[list, sympy.Function]", - "default": "[-sympy.oo]", - "description": "" - }, - { - "name": "shear_viscosity_max", - "type_hint": "Union[list, sympy.Function]", - "default": "[sympy.oo]", - "description": "" - }, - { - "name": "yield_stress", - "type_hint": "Union[list, sympy.Function]", - "default": "[sympy.oo]", - "description": "" - }, - { - "name": "yield_stress_min", - "type_hint": "Union[list, sympy.Function]", - "default": "[-sympy.oo]", - "description": "" - }, - { - "name": "strainrate_inv_II", - "type_hint": "sympy.Function", - "default": "sympy.oo", - "description": "" - }, - { - "name": "strainrate_inv_II_min", - "type_hint": "float", - "default": "0.0", - "description": "" - }, - { - "name": "averaging_method", - "type_hint": "str", - "default": "'HA'", - "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Yield live ``on_remesh(ctx)`` callbacks registered on the mesh.\n\nHooks are stored as weakrefs to the registering operator; this\niterator drops dead refs and yields a bound callable for each live\none. Defined here (rather than as a Mesh method) so the helper\nmodule can be imported without importing the entire Mesh stack.", + "harvested_comments": [ + "Prune dead refs once per dispatch." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 819, - "signature": "(inner_self, materialIndex: Union[uw.swarm.SwarmVariable, uw.discretisation.MeshVariable] = None, shear_viscosity_0: Union[list, sympy.Function] = [1], shear_modulus: Union[list, sympy.Function] = [sympy.oo], shear_viscosity_min: Union[list, sympy.Function] = [-sympy.oo], shear_viscosity_max: Union[list, sympy.Function] = [sympy.oo], yield_stress: Union[list, sympy.Function] = [sympy.oo], yield_stress_min: Union[list, sympy.Function] = [-sympy.oo], strainrate_inv_II: sympy.Function = sympy.oo, strainrate_inv_II_min: float = 0.0, averaging_method: str = 'HA', stress_star: sympy.Function = None, stress_star_star: sympy.Function = None, dt_elastic: Union[float, sympy.Function] = [sympy.oo])", + "name": "_remap_var_set", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 385, + "signature": "(mesh, vars_, old_X, new_X, old_data)", "parameters": [ { - "name": "inner_self", + "name": "mesh", "type_hint": null, "default": null, "description": "" }, { - "name": "materialIndex", - "type_hint": "Union[uw.swarm.SwarmVariable, uw.discretisation.MeshVariable]", - "default": "None", - "description": "" - }, - { - "name": "shear_viscosity_0", - "type_hint": "Union[list, sympy.Function]", - "default": "[1]", - "description": "" - }, - { - "name": "shear_modulus", - "type_hint": "Union[list, sympy.Function]", - "default": "[sympy.oo]", - "description": "" - }, - { - "name": "shear_viscosity_min", - "type_hint": "Union[list, sympy.Function]", - "default": "[-sympy.oo]", - "description": "" - }, - { - "name": "shear_viscosity_max", - "type_hint": "Union[list, sympy.Function]", - "default": "[sympy.oo]", - "description": "" - }, - { - "name": "yield_stress", - "type_hint": "Union[list, sympy.Function]", - "default": "[sympy.oo]", - "description": "" - }, - { - "name": "yield_stress_min", - "type_hint": "Union[list, sympy.Function]", - "default": "[-sympy.oo]", - "description": "" - }, - { - "name": "strainrate_inv_II", - "type_hint": "sympy.Function", - "default": "sympy.oo", - "description": "" - }, - { - "name": "strainrate_inv_II_min", - "type_hint": "float", - "default": "0.0", - "description": "" - }, - { - "name": "averaging_method", - "type_hint": "str", - "default": "'HA'", + "name": "vars_", + "type_hint": null, + "default": null, "description": "" }, { - "name": "stress_star", - "type_hint": "sympy.Function", - "default": "None", + "name": "old_X", + "type_hint": null, + "default": null, "description": "" }, { - "name": "stress_star_star", - "type_hint": "sympy.Function", - "default": "None", + "name": "new_X", + "type_hint": null, + "default": null, "description": "" }, { - "name": "dt_elastic", - "type_hint": "Union[float, sympy.Function]", - "default": "[sympy.oo]", + "name": "old_data", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "One-shot REMAP dance for a set of variables.\n\nUsed by the generic per-variable pass in\n:func:`remesh_with_field_transfer` AND exposed (as\n:func:`remap_var_set`) so an operator's ``on_remesh`` hook can\nforce-REMAP its CARRY-managed vars on an adapt that is\nALE-incompatible (e.g. an OT_adapt reset, where the linear\n``\u0394x/dt \u2192 v_mesh`` interpretation breaks down).\n\nContract: on entry the mesh is at ``new_X`` and each var's\n``.data`` may hold *either* the original snapshot value (CARRY:\noperator-managed vars that the generic pass skipped) or the\nnot-yet-restored value (REMAP: the snapshot has not been written\nback). ``old_data`` is the snapshot keyed by var, captured *before*\n``do_move`` ran. On exit the mesh is back at ``new_X`` and every\nvar's ``.data`` holds the OLD field evaluated at the var's NEW DOF\ncoordinates.\n\nThe set may be empty (no-op). Operations are MPI-collective; every\nrank must call with the same set.", + "harvested_comments": [ + "Capture target DOF coords on the NEW mesh first (we are sitting", + "on new_X right now). var.coords reads from the live mesh state.", + "Deform back to the old coords, restore data so global_evaluate", + "sees the old field, evaluate at each var's new DOF coords,", + "then deform forward and write the resampled values back." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_Parameters", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1293, - "signature": "(self, dim)", + "name": "_mark_reinit_stale", + "kind": "function", + "file": "src/underworld3/discretisation/remesh.py", + "line": 478, + "signature": "(var)", "parameters": [ { - "name": "dim", + "name": "var", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Mark a REINIT variable stale.\n\nPhase 1: a hook for the future. The current REINIT-stamped vars\n(``_n_proj``, projection auxiliaries) recompute themselves on the\nnext access path that owns them \u2014 there is nothing eager to do here\nthat the lazy recomputation does not already cover. Kept as a\nsingle call site so future eager invalidation (e.g. zero ``.data``,\nor set a ``_needs_recompute`` flag the consumer checks) has one\nplace to land.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": null, "is_public": false }, { - "name": "__init__", + "name": "__cinit__", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1495, - "signature": "(inner_self, diffusivity: Union[float, sympy.Function] = 1)", - "parameters": [ - { - "name": "inner_self", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "diffusivity", - "type_hint": "Union[float, sympy.Function]", - "default": "1", - "description": "" - } - ], + "file": "src/underworld3/function/_dminterp_wrapper.pyx", + "line": 72, + "signature": "def __cinit__(self):", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Initialize - structure not yet created.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": null, "is_public": false }, { - "name": "__init__", + "name": "__dealloc__", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1510, - "signature": "(self, dim)", - "parameters": [ - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/function/_dminterp_wrapper.pyx", + "line": 222, + "signature": "def __dealloc__(self):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Cleanup when Python GC destroys this object.\n\n This is called automatically when the cache entry is deleted\n or when the cache is cleared.\n", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": null, + "is_public": false + }, + { + "name": "_collect_mesh_varfns", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 222, + "signature": "def _collect_mesh_varfns(mesh):", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "\n Collect all mesh variable function symbols from a mesh.\n\n Parameters\n ----------\n mesh : Mesh\n The mesh containing variables\n\n Returns\n -------\n set\n Set of UnderworldAppliedFunction symbols for all mesh variables\n", "harvested_comments": [], - "status": "none", + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_lambdify_and_evaluate", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 244, + "signature": "def _lambdify_and_evaluate(expr, coords, interpolated_results, coord_sys=None, mesh=None):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Substitute interpolated values and evaluate expression via lambdify.\n\n This is the shared final step for both PETSc and RBF evaluation paths.\n It replaces mesh variable symbols with placeholder symbols, creates a\n lambdified function, and evaluates it with the interpolated values.\n\n Parameters\n ----------\n expr : sympy expression\n Expression to evaluate (already simplified/unwrapped)\n coords : ndarray\n Coordinates array, shape (n_points, dim)\n interpolated_results : dict\n Mapping from varfn symbols to interpolated value arrays\n coord_sys : CoordSys3D, optional\n Coordinate system to use\n mesh : Mesh, optional\n Mesh for coordinate system fallback\n\n Returns\n -------\n ndarray\n Evaluated results, shape (n_points, *expr_shape)\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_project_to_work_variable", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 648, + "signature": "def _project_to_work_variable(expr, mesh, smoothing=1e-6):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Project expression to a work variable, resolving derivatives to nodal values.\n\n This is used to handle derivative expressions before the interior/exterior\n split in evaluate_nd. The work variable can then be interpolated using\n standard DMInterpolation (interior) and RBF (exterior) paths.\n\n Parameters\n ----------\n expr : sympy expression\n Expression to project (may contain derivatives)\n mesh : Mesh\n The mesh\n smoothing : float\n Projection smoothing parameter\n\n Returns\n -------\n MeshVariable\n Work variable containing projected nodal values\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_clement_to_work_variable", + "kind": "function", + "file": "src/underworld3/function/_function.pyx", + "line": 772, + "signature": "def _clement_to_work_variable(expr, mesh, derivfns):", + "parameters": [], + "returns": null, + "existing_docstring": "\n Evaluate expression at nodes using Clement gradient recovery (no solve).\n\n This is the quick/dirty path for derivative evaluation. Instead of\n projecting (which requires a solve), we:\n 1. Compute Clement gradient at mesh nodes for each derivative\n 2. Get mesh variable values at nodes\n 3. Lambdify and evaluate the full expression at nodes\n 4. Store result in work variable\n\n Parameters\n ----------\n expr : sympy expression\n Expression containing derivatives\n mesh : Mesh\n The mesh\n derivfns : dict\n Dictionary mapping source variables to their derivative expressions\n\n Returns\n -------\n MeshVariable\n Work variable containing expression values at nodes\n", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_is_enabled", + "kind": "method", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 50, + "signature": "(self) -> bool", + "parameters": [], + "returns": "bool", + "existing_docstring": "Check if caching is enabled.", + "harvested_comments": [ + "Check mesh flag (default: True)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "DiffusionModel", + "parent_class": "DMInterpolationCache", "is_public": false }, { - "name": "__init__", + "name": "_hash_coords", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1580, - "signature": "(inner_self, eta_0: Union[float, sympy.Function] = 1, eta_1: Union[float, sympy.Function] = 1, director: Union[sympy.Matrix, sympy.Function] = sympy.Matrix([0, 0, 1]))", + "file": "src/underworld3/function/dminterpolation_cache.py", + "line": 119, + "signature": "(self, coords: np.ndarray) -> int", "parameters": [ { - "name": "inner_self", - "type_hint": null, + "name": "coords", + "type_hint": "np.ndarray", "default": null, "description": "" - }, - { - "name": "eta_0", - "type_hint": "Union[float, sympy.Function]", - "default": "1", - "description": "" - }, - { - "name": "eta_1", - "type_hint": "Union[float, sympy.Function]", - "default": "1", - "description": "" - }, - { - "name": "director", - "type_hint": "Union[sympy.Matrix, sympy.Function]", - "default": "sympy.Matrix([0, 0, 1])", - "description": "" } ], - "returns": null, - "existing_docstring": null, + "returns": "int", + "existing_docstring": "Fast coordinate hashing for cache lookups.\n\nUses xxhash for speed (already used in old caching system).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_Parameters", + "parent_class": "DMInterpolationCache", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1629, - "signature": "(self, dim)", + "name": "_unwrap_atom", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 76, + "signature": "(atom, mode = 'nondimensional')", "parameters": [ { - "name": "dim", + "name": "atom", "type_hint": null, "default": null, "description": "" + }, + { + "name": "mode", + "type_hint": null, + "default": "'nondimensional'", + "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Extract the value from a single atom based on mode.\n\nArgs:\n atom: UWexpression, UWQuantity, UWCoordinate, or other symbol\n mode: 'nondimensional' - use .data for ND values (JIT/evaluate)\n 'dimensional' - use .value for display\n 'symbolic' - use .sym for symbolic substitution\n 'symbolic_keep_constants' - like 'symbolic', but stop at truly\n constant UWexpressions (eta0, tau_y, ...), leaving them as the\n *same* symbol object so the JIT constants[] mechanism still\n routes them. Used to unwrap F0/F1 *before* the Jacobian\n derivative so field/grad-v dependence of the viscosity is\n differentiated (full Newton) while constants stay symbolic.\n\nReturns:\n The unwrapped value (float, sympy.Number, or sympy expression)", "harvested_comments": [ - "default values ... maybe ??" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "UWCoordinate: always unwrap to BaseScalar (placeholder for evaluation)", + "UWexpression", + "User display: show dimensional value", + "JIT/evaluate: non-dimensionalize if scaling active", + "Recursively unwrap to get inner expression" ], - "parent_class": "TransverseIsotropicFlowModel", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1694, - "signature": "(self, material_swarmVariable: Optional[IndexSwarmVariable] = None, constitutive_models: Optional[list] = [])", + "name": "_unwrap_expression_once", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 157, + "signature": "(expr, mode = 'nondimensional')", "parameters": [ { - "name": "material_swarmVariable", - "type_hint": "Optional[IndexSwarmVariable]", - "default": "None", + "name": "expr", + "type_hint": null, + "default": null, "description": "" }, { - "name": "constitutive_models", - "type_hint": "Optional[list]", - "default": "[]", + "name": "mode", + "type_hint": null, + "default": "'nondimensional'", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Single substitution pass over all UW atoms in an expression.\n\nUses free_symbols for reliable iteration (avoids issues with atoms()).\n\nArgs:\n expr: SymPy expression possibly containing UW atoms\n mode: See _unwrap_atom\n\nReturns:\n Expression with UW atoms substituted", + "harvested_comments": [ + "Handle non-expression types directly", + "Unwrap the UWexpression itself first", + "Build substitution dict for all UW atoms" ], - "parent_class": "MultiMaterial", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 93, - "signature": "(self, index, system, pretty_str = None, latex_str = None, mesh = None, axis_index = None)", + "name": "_unwrap_expressions", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 397, + "signature": "(fn, keep_constants = True, return_self = True)", "parameters": [ { - "name": "index", + "name": "fn", "type_hint": null, "default": null, "description": "" }, { - "name": "system", + "name": "keep_constants", "type_hint": null, - "default": null, + "default": "True", "description": "" }, { - "name": "pretty_str", + "name": "return_self", "type_hint": null, - "default": "None", + "default": "True", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Main unwrapping logic for JIT compilation.\n\nDEPRECATED: Use unwrap_expression(fn, mode='nondimensional') instead.\nThis function is preserved for backward compatibility.", + "harvested_comments": [ + "Use unified implementation" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_unwrap_for_compilation", + "kind": "function", + "file": "src/underworld3/function/expressions.py", + "line": 408, + "signature": "(fn, keep_constants = True, return_self = True)", + "parameters": [ { - "name": "latex_str", + "name": "fn", "type_hint": null, - "default": "None", + "default": null, "description": "" }, { - "name": "mesh", + "name": "keep_constants", "type_hint": null, - "default": "None", + "default": "True", "description": "" }, { - "name": "axis_index", + "name": "return_self", "type_hint": null, - "default": "None", + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "INTERNAL ONLY: Unwrap UW expressions to pure SymPy for JIT compilation.\n\nDEPRECATED: Use unwrap_expression(fn, mode='nondimensional') instead.", "harvested_comments": [ - "Store UW3-specific attributes", - "Cache the original BaseScalar for equality comparison", - "This is what makes sympy.diff() work!", - "Track for debugging" + "Handle UWDerivativeExpression specially" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": null, "is_public": false }, { - "name": "__init__", + "name": "_hashable_content", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1379, - "signature": "(self, mesh, system: Optional[CoordinateSystemType] = CoordinateSystemType.CARTESIAN)", + "file": "src/underworld3/function/expressions.py", + "line": 731, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Include _uw_id in hash so symbols with same name but different IDs are distinct.\n\nThis follows the same pattern as sympy.Dummy which uses dummy_index.\nWhen _uw_id is None, symbols match by name alone (shared identity).\nWhen _uw_id is set, symbols with same name but different IDs are distinct.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__getnewargs_ex__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 744, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Support pickling by including _uw_id in reconstruction args.", + "harvested_comments": [ + "Return args and kwargs needed to reconstruct this symbol" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "_latex", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 789, + "signature": "(self, printer)", "parameters": [ { - "name": "mesh", + "name": "printer", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Custom LaTeX representation using _display_name.\n\nThis method is called by SymPy's LaTeX printer to get the\nrepresentation of this symbol. By overriding it, we can\ncontrol how the expression appears in LaTeX output without\nchanging its symbolic identity.\n\nParameters\n----------\nprinter : LatexPrinter\n The SymPy LaTeX printer instance (not used, but required by protocol).\n\nReturns\n-------\nstr\n The LaTeX representation of this expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "_sympystr", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 810, + "signature": "(self, printer)", + "parameters": [ { - "name": "system", - "type_hint": "Optional[CoordinateSystemType]", - "default": "CoordinateSystemType.CARTESIAN", + "name": "printer", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Custom string representation using _display_name.\n\nThis method is called by SymPy's string printer to get the\nrepresentation of this symbol. Like _latex(), this allows\ncustomizing display without affecting identity.\n\nParameters\n----------\nprinter : StrPrinter\n The SymPy string printer instance (not used, but required by protocol).\n\nReturns\n-------\nstr\n The string representation of this expression.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "_compute_nondimensional_value", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 956, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Internal: compute the non-dimensional value from the wrapped object.\n\nThis is the machinery that .data uses. Named explicitly to be self-documenting.", "harvested_comments": [ - "Guard against SymPy trying to construct a CoordinateSystem from sympified elements", - "SymPy may iterate over the object and try to recreate it from elements", - "are the mesh coordinates XYZ or have they been replaced by", - "\"Natural\" coordinates like r, theta, z ?", - "Get the raw BaseScalars from the mesh" + "TRANSPARENT CONTAINER: Derive from _sym (the wrapped object)", + "Delegate to wrapped object's .data", + "Fallback to dimensional value" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "UWexpression", "is_public": false }, { - "name": "_setup_discretisation", + "name": "_sympy_", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 1524, - "signature": "def _setup_discretisation(self, verbose=False):", + "file": "src/underworld3/function/expressions.py", + "line": 1116, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "\n Most of what is in the init phase that is not called by _setup_terms()\n", + "existing_docstring": "SymPy protocol - return self (we ARE a Symbol).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UWexpression", "is_public": false }, { - "name": "_setup_discretisation", + "name": "_sympify_", "kind": "method", - "file": "src/underworld3/cython/petsc_generic_snes_solvers.pyx", - "line": 2773, - "signature": "def _setup_discretisation(self, verbose=False):", + "file": "src/underworld3/function/expressions.py", + "line": 1120, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "\n Most of what is in the init phase that is not called by _setup_terms()\n", + "existing_docstring": "SymPy sympify protocol - return self.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__bool__", "kind": "method", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 46, - "signature": "def __init__( self,\n mesh: underworld3.discretisation.Mesh,\n fn: Union[float, int, sympy.Basic] ):", + "file": "src/underworld3/function/expressions.py", + "line": 1128, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Always True for boolean contexts.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__hash__", "kind": "method", - "file": "src/underworld3/cython/petsc_maths.pyx", - "line": 238, - "signature": "def __init__( self,\n mesh: underworld3.discretisation.Mesh,\n fn: Union[float, int, sympy.Basic] ):", + "file": "src/underworld3/function/expressions.py", + "line": 1132, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Delegate to Symbol's hash.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__eq__", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 208, - "signature": "(self, plex_or_meshfile, degree = 1, simplex = True, coordinate_system_type = None, qdegree = 2, markVertices = None, useRegions = None, useMultipleTags = None, filename = None, refinement = None, refinement_callback = None, coarsening = None, coarsening_callback = None, return_coords_to_bounds = None, boundaries = None, boundary_normals = None, name = None, units = None, verbose = False, *args, **kwargs)", + "file": "src/underworld3/function/expressions.py", + "line": 1136, + "signature": "(self, other)", "parameters": [ { - "name": "plex_or_meshfile", + "name": "other", "type_hint": null, "default": null, "description": "" - }, - { - "name": "degree", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "simplex", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "coordinate_system_type", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "qdegree", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "markVertices", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "useRegions", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "useMultipleTags", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "filename", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "refinement", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "refinement_callback", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "coarsening", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "coarsening_callback", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "return_coords_to_bounds", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "boundaries", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "boundary_normals", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "name", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "units", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Delegate to Symbol's equality (symbolic identity).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__ne__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1140, + "signature": "(self, other)", + "parameters": [ { - "name": "*args", + "name": "other", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Delegate to Symbol's inequality.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__mul__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1211, + "signature": "(self, other)", + "parameters": [ { - "name": "**kwargs", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Multiplication - return UWexpression to preserve units.", "harvested_comments": [ - "Get coordinate units from model (not user parameter)", - "The model owns the unit system - all meshes use the same units", - "Ignore user-provided units parameter, get from model instead", - "Set units from model", - "Lock model units now that a mesh has been created" + "Handle matrix cases", + "Use applyfunc to multiply each element by self (as Symbol).", + "This preserves unit tracking: result is Matrix([x * self, ...])", + "where get_units() can find units for both self AND matrix elements.", + "DON'T use self._sym * other - that loses matrix element units!" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__rmul__", "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 34, - "signature": "(self, mesh, name: str = 'default')", + "file": "src/underworld3/function/expressions.py", + "line": 1253, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", + "name": "other", "type_hint": null, "default": null, "description": "" - }, - { - "name": "name", - "type_hint": "str", - "default": "'default'", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Right multiplication - handle UWQuantity, Matrix, and scalars.\n\nThis is called when `other * self` fails (other.__mul__ returns NotImplemented).\nIn particular, `MutableDenseMatrix * UWexpression` triggers this because\nSymPy matrices don't understand UWexpression.", "harvested_comments": [ - "Stores CachedDMInterpolationInfo" + "Handle UWQuantity - preserve units", + "Handle SymPy Matrix types - delegate to forward multiplication", + "Matrix * scalar works when scalar is on the left, so use self * other", + "which calls our __mul__ \u2192 Symbol.__mul__ \u2192 SymPy handles it correctly", + "scalar * Matrix is handled by SymPy - returns matrix with scaled elements" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__truediv__", "kind": "method", "file": "src/underworld3/function/expressions.py", - "line": 741, - "signature": "(self, name, sym = None, description = 'No description provided', value = None, units = None, **kwargs)", + "line": 1283, + "signature": "(self, other)", "parameters": [ { - "name": "name", + "name": "other", "type_hint": null, "default": null, "description": "" - }, - { - "name": "sym", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "description", - "type_hint": null, - "default": "'No description provided'", - "description": "" - }, - { - "name": "value", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "units", - "type_hint": null, - "default": "None", - "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Division - return UWexpression to preserve units.", + "harvested_comments": [ + "Handle UWQuantity - use full Pint arithmetic", + "Use FULL Pint quantity arithmetic", + "Handle UWexpression - preserve LAZY evaluation by returning SymPy quotient", + "Same design as __mul__: return raw SymPy quotient, derive units on demand", + "Scalar division - preserve self's units" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__rtruediv__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1336, + "signature": "(self, other)", + "parameters": [ { - "name": "**kwargs", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Right division - handle UWQuantity to preserve units.", "harvested_comments": [ - "Legacy parameter", - "Units for wrapping the value", - "Handle legacy 'value' parameter", - "If units are provided and sym is a plain numeric value, wrap it in UWQuantity", - "Don't wrap if sym is already a UWQuantity, UWexpression, or SymPy expression" + "Scalar / expression - units become inverted", + "Only handle if units is a Pint unit object (not None or string)" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__add__", "kind": "method", "file": "src/underworld3/function/expressions.py", - "line": 1679, - "signature": "(self, expr, *args, **kwargs)", + "line": 1357, + "signature": "(self, other)", "parameters": [ { - "name": "expr", + "name": "other", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Addition - handle UWQuantity and UWexpression with unit conversion.", + "harvested_comments": [ + "Handle UWexpression + UWexpression", + "TRANSPARENT CONTAINER: If self.sym is UWQuantity, use proper Pint arithmetic", + "Both contain UWQuantity - use proper unit-aware addition", + "Pint will handle 10cm + 1m = 110cm automatically", + "Pass full UWQuantity - Transparent Container stores it" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__radd__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1433, + "signature": "(self, other)", + "parameters": [ { - "name": "*args", + "name": "other", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Right addition - handle UWQuantity specially.", + "harvested_comments": [ + "Addition is commutative" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__sub__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1440, + "signature": "(self, other)", + "parameters": [ { - "name": "**kwargs", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Subtraction - LAZY EVALUATION pattern.\n\nWhen subtracting UWexpressions, we preserve both symbols in the tree\nrather than doing eager arithmetic. This allows unwrap_for_evaluate()\nto substitute the correct nondimensional values later.\n\nThe key insight is that if one operand is a coordinate (no units) and\nthe other has units, we CANNOT do the subtraction eagerly because:\n- Coordinates are already in ND form (from mesh scaling)\n- Unit-bearing quantities need to be nondimensionalized by .data\n\nBy keeping both symbols, unwrap_for_evaluate can process each one\ncorrectly according to its type.", + "harvested_comments": [ + "Handle UWexpression - UWexpression: LAZY EVALUATION", + "Keep both symbols in the tree - don't do eager arithmetic", + "Delegate to SymPy Symbol subtraction - preserves both symbols", + "Handle UWexpression - UWQuantity: Wrap in UWexpression first (LAZY)", + "Wrap the UWQuantity in a UWexpression to preserve it as a symbol" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWDerivativeExpression", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__rsub__", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 150, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/function/expressions.py", + "line": 1480, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Right subtraction - handle UWQuantity specially.", "harvested_comments": [ - "region_id -> material_name", - "Material change callbacks" + "UWQuantity - UWexpression \u2192 UWexpression", + "Convert self to other's units (other is the \"base\" unit here)", + "Convert self's value to other's units for correct subtraction", + "other - self: result is in other's units", + "Use self.value (not self.sym) for arithmetic" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "MaterialRegistry", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__pow__", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 63, - "signature": "(self, mesh)", + "file": "src/underworld3/function/expressions.py", + "line": 1512, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Power - delegate to Symbol.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "mesh_vector_calculus", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__rpow__", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 290, - "signature": "(self, mesh)", + "file": "src/underworld3/function/expressions.py", + "line": 1516, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Right power - delegate to Symbol.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__neg__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1520, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Negation - delegate to Symbol.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1528, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "User-friendly representation showing value with units.\n\nFor expressions with units, shows: value [units]\nFor expressions with symbolic content, shows: name = symbolic_expr\nFor named expressions with simple values, shows: name = value [units]\n\nNote: Uses _display_name (set via rename()) rather than _given_name\nso that renamed expressions show their custom names.", "harvested_comments": [ - "NATIVE coordinate systems deprecated, always warn" + "Use _display_name for representation (respects rename())", + "Check if this is a \"named\" expression (user-defined name vs auto-generated)", + "Auto-generated names start with (", + "Format the value part", + "Has units - show value with units" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "mesh_vector_calculus_cylindrical", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "__str__", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 616, - "signature": "(self, mesh)", - "parameters": [ - { - "name": "mesh", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/function/expressions.py", + "line": 1568, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "String representation showing value with units if available.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "mesh_vector_calculus_spherical", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "_repr_latex_", "kind": "method", - "file": "src/underworld3/maths/vector_calculus.py", - "line": 807, - "signature": "(self, mesh)", - "parameters": [ - { - "name": "mesh", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/function/expressions.py", + "line": 1577, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "LaTeX representation for Jupyter notebooks.\n\nJupyter prioritizes _repr_latex_ over __repr__, so we override\nSymPy's default to show units.", + "harvested_comments": [ + "Check if this is a \"named\" expression (user-defined name vs auto-generated)", + "Format value for LaTeX", + "Use scientific notation for very small/large numbers", + "Format units for LaTeX (Pint units have LaTeX-compatible format)", + "For named expressions, show name = value [units]" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], + "parent_class": "UWexpression", + "is_public": false + }, + { + "name": "_repr_html_", + "kind": "method", + "file": "src/underworld3/function/expressions.py", + "line": 1622, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "HTML representation for Jupyter notebooks (fallback if LaTeX not available).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "mesh_vector_calculus_spherical_surface2D_lonlat", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "_repr_png_", "kind": "method", - "file": "src/underworld3/mpi.py", - "line": 387, - "signature": "(self, pattern = 'collective', returnobj = None)", - "parameters": [ - { - "name": "pattern", - "type_hint": null, - "default": "'collective'", - "description": "" - }, - { - "name": "returnobj", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/function/expressions.py", + "line": 1645, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Disable PNG rendering to ensure _repr_latex_ is used.\n\nSymPy's init_printing() may enable PNG rendering which bypasses\nour custom _repr_latex_. By returning None, we force Jupyter to\nfall back to text/latex format.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "call_pattern", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "_repr_svg_", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 141, + "file": "src/underworld3/function/expressions.py", + "line": 1655, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Parameter change callbacks", - "Parameter change history" - ], - "status": "none", + "existing_docstring": "Disable SVG rendering to ensure _repr_latex_ is used.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ParameterRegistry", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "_repr_mimebundle_", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 151, - "signature": "(self, name, swarm, size = None, vtype = None, dtype = float, proxy_degree = 1, proxy_continuous = True, _register = True, _proxy = True, _nn_proxy = False, varsymbol = None, rebuild_on_cycle = True, units = None, units_backend = None)", + "file": "src/underworld3/function/expressions.py", + "line": 1659, + "signature": "(self, **kwargs)", "parameters": [ { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "swarm", + "name": "**kwargs", "type_hint": null, "default": null, "description": "" - }, - { - "name": "size", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "vtype", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "dtype", - "type_hint": null, - "default": "float", - "description": "" - }, - { - "name": "proxy_degree", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "proxy_continuous", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "_register", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "_proxy", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "_nn_proxy", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "varsymbol", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "rebuild_on_cycle", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "units", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "units_backend", - "type_hint": null, - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "MIME bundle for Jupyter display - highest priority representation.\n\nThis method has ABSOLUTE HIGHEST PRIORITY in Jupyter's display system.\nIt overrides ANY type-based formatters (including SymPy's init_printing()).\n\nWhy this is needed:\n- SymPy's init_printing() registers formatters for sympy.Basic types\n- UWexpression inherits from sympy.Symbol (a sympy.Basic subclass)\n- Without this, SymPy's formatter renders UWexpression as raw symbols\n- _repr_mimebundle_ cannot be overridden by type formatters\n\nReturns dict of MIME type \u2192 content for display.", "harvested_comments": [ - "only needed if MATRIX type", - "Store unit metadata for variable", - "Convert string units to Pint Unit objects for consistency with MeshVariable", - "Parse string units to Pint Unit object", - "uw.units('K') returns a Quantity (1 kelvin), so we extract .units to get the Unit" + "Get our custom LaTeX representation", + "Also provide plain text fallback" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "_ipython_display_", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1943, - "signature": "(self, name, swarm, indices = 1, proxy_degree = 1, proxy_continuous = True, update_type = 0, npoints = 5, radius = 0.5, npoints_bc = 2, ind_bc = None, varsymbol = None)", - "parameters": [ - { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "swarm", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "indices", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "proxy_degree", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "proxy_continuous", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "update_type", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "npoints", - "type_hint": null, - "default": "5", - "description": "" - }, - { - "name": "radius", - "type_hint": null, - "default": "0.5", - "description": "" - }, - { - "name": "npoints_bc", - "type_hint": null, - "default": "2", - "description": "" - }, - { - "name": "ind_bc", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "varsymbol", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/function/expressions.py", + "line": 1685, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "IPython/Jupyter display hook - ABSOLUTE highest priority.\n\nThis method OVERRIDES MathematicalMixin._ipython_display_ to show\nour custom representation with units instead of raw SymPy symbols.\n\nWhy this override is needed:\n- MathematicalMixin._ipython_display_ calls display(Math(latex(sym)))\n- This shows only the symbol name without units\n- We want to show value + units for UWexpressions", "harvested_comments": [ - "**2 # changed to radius", - "These are the things we require of the generic swarm variable type", - "The indices variable defines how many \"level set\" maps we create as components in the proxy variable", - "Initialize lazy evaluation state", - "Proxy variables need initial update" + "Use our custom LaTeX representation with units", + "IPython not available - silent fallback" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", + "name": "_object_viewer", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2308, - "signature": "(self, mesh, recycle_rate = 0, verbose = False, clip_to_mesh = True)", - "parameters": [ - { - "name": "mesh", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "recycle_rate", - "type_hint": null, - "default": "0", - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "clip_to_mesh", - "type_hint": null, - "default": "True", - "description": "" - } - ], + "file": "src/underworld3/function/expressions.py", + "line": 1707, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Display expression details for solver .view() \"Where:\" section.\n\nShows the expression name, its symbolic value, and description.\nCalled by solver _object_viewer when expanding expression definitions.", "harvested_comments": [ - "Store reference to model instead of direct mesh reference", - "This enables dynamic mesh handover while maintaining access to mesh services", - "Register mesh with model if not already present", - "Store reference to this swarm's specific mesh for proxy operations", - "Mesh version tracking for coordinate change detection" + "Build LaTeX representation: symbol = value", + "Get the symbolic value", + "Format the value", + "Strip $ signs if present (we'll add our own)", + "Display: name = value (description)" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Swarm", + "parent_class": "UWexpression", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 4310, - "signature": "(self, trackedVariable: uw.discretisation.MeshVariable, verbose = False)", + "name": "_repack_tensor_to_paraview", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 128, + "signature": "(data, vtype, dim)", "parameters": [ { - "name": "trackedVariable", - "type_hint": "uw.discretisation.MeshVariable", + "name": "data", + "type_hint": null, "default": null, "description": "" }, { - "name": "verbose", + "name": "vtype", "type_hint": null, - "default": "False", + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Repack UW3 tensor data to ParaView 9-component (3x3) format.\n\nThe checkpoint path (``/fields/``) stores tensors in UW3's internal\n``_data_layout`` ordering. The visualisation path\n(``/vertex_fields/``) must repack to ParaView's expected row-major\n3x3 layout: ``[xx, xy, xz, yx, yy, yz, zx, zy, zz]``.\n\nUW3 ``_data_layout`` ordering:\n\n- TENSOR 2D: ``[xx, xy, yx, yy]`` (row-major, 4 components)\n- SYM_TENSOR 2D: ``[xx, yy, xy]`` (3 unique components)\n- TENSOR 3D: ``[xx, xy, xz, yx, yy, yz, zx, zy, zz]``\n (row-major, already ParaView-compatible)\n- SYM_TENSOR 3D: ``[xx, yy, zz, xy, xz, yz]`` (6 unique components)\n\n.. note::\n\n The original PR #69 assumed 2D TENSOR stored ``[xx, yy, xy, yx]``.\n This corrected version uses the actual ``_data_layout`` row-major\n ordering ``[xx, xy, yx, yy]``.", "harvested_comments": [ - "Set up a standard swarm", - "The launch point location", - "The launch point index", - "The launch point processor rank", - "PETSc < 3.24 has an off-by-one bug in addNPoints when swarm size is initially zero" + "69 assumed 2D TENSOR stored ``[xx, yy, xy, yx]``.", + "[xx, xy, yx, yy] \u2192 [xx, xy, 0, yx, yy, 0, 0, 0, 0]", + "unknown layout, pass through", + "[xx, yy, xy] \u2192 [xx, xy, 0, xy, yy, 0, 0, 0, 0]", + "xy (symmetric)" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "NodalPointSwarm", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 143, - "signature": "(self, mesh, recycle_rate = 0, verbose = False)", + "name": "_write_vec_to_group", + "kind": "function", + "file": "src/underworld3/function/field_projection.py", + "line": 194, + "signature": "(viewer, data_array, name, group, comm)", "parameters": [ { - "name": "mesh", + "name": "viewer", "type_hint": null, "default": null, "description": "" }, { - "name": "recycle_rate", + "name": "data_array", "type_hint": null, - "default": "0", + "default": null, "description": "" }, { - "name": "verbose", + "name": "name", "type_hint": null, - "default": "False", + "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Is the swarm a streak-swarm ?", - "dictionary for variables", - "import weakref (not helpful as garbage collection does not remove the fields from the DM)", - "self._vars = weakref.WeakValueDictionary()", - "add variable to handle particle coords - predefined by DMSwarm, expose to UW" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "PICSwarm", - "is_public": false - }, - { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1396, - "signature": "(self, trackedVariable: uw.discretisation.MeshVariable, verbose = False)", - "parameters": [ + }, { - "name": "trackedVariable", - "type_hint": "uw.discretisation.MeshVariable", + "name": "group", + "type_hint": null, "default": null, "description": "" }, { - "name": "verbose", + "name": "comm", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Set up a standard swarm", - "proxy_degree=trackedVariable.degree,", - "proxy_continuous=trackedVariable.continuous,", - "The launch point location", - "The launch point index" - ], - "status": "none", + "existing_docstring": "Write a numpy array as a standalone PETSc Vec under an HDF5 group.\n\nDM-associated Vecs ignore ``pushGroup`` because the DM's HDF5 writer\npushes its own ``/fields/`` prefix. This helper creates a plain Vec\n(no DM) so ``pushGroup`` is respected.\n\nWhen *data_array* is 2D ``(N, ncomp)``, the Vec block size is set to\n*ncomp* so the HDF5 dataset is written as ``(N, ncomp)`` rather than\nflat ``(N*ncomp,)``.\n\nParameters\n----------\nviewer\n An open ``PETSc.ViewerHDF5``.\ndata_array : np.ndarray\n Local (owned) data to write \u2014 shape ``(n_local,)`` or\n ``(n_local, ncomp)``.\nname : str\n Dataset name in the HDF5 group.\ngroup : str\n HDF5 group path (e.g. ``/vertex_fields``).\ncomm\n MPI communicator.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "NodalPointPICSwarm", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 68, - "signature": "(self, psi_fn: sympy.Basic, theta: Optional[float] = 0.5, varsymbol: Optional[str] = '\\\\psi', verbose: Optional[bool] = False, bcs = [], order: int = 1, smoothing: float = 0.0)", + "name": "_validate_coords_not_sequence", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 32, + "signature": "(coords)", "parameters": [ { - "name": "psi_fn", - "type_hint": "sympy.Basic", - "default": null, - "description": "" - }, - { - "name": "theta", - "type_hint": "Optional[float]", - "default": "0.5", - "description": "" - }, - { - "name": "varsymbol", - "type_hint": "Optional[str]", - "default": "'\\\\psi'", - "description": "" - }, - { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", - "description": "" - }, - { - "name": "bcs", + "name": "coords", "type_hint": null, - "default": "[]", - "description": "" - }, - { - "name": "order", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "smoothing", - "type_hint": "float", - "default": "0.0", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "a sympy expression for \u03c8; can be scalar or matrix", - "Ensure psi_fn is a sympy Matrix.", - "stored with its native shape", - "capture the shape", - "Set the display symbol for psi_fn and for the history variable." - ], - "status": "none", + "existing_docstring": "Reject Python list/tuple coordinate input with a clear error.\n\nCoordinate lists \u2014 in particular quantity-valued lists such as\n``[(x_qty, y_qty)]`` \u2014 are NOT a supported coordinate form\n(maintainer ruling D7, 2026-07-06: the coordinate-units family is\nunsupported). Supported forms are documented on :func:`evaluate`.\nWithout this guard a list falls through to\n``uw.non_dimensionalise(list)`` whose error message does not tell\nthe user what to pass instead.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Symbolic", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 254, - "signature": "(self, mesh: uw.discretisation.Mesh, psi_fn: Union[uw.discretisation.MeshVariable, sympy.Basic], vtype: uw.VarType, degree: int, continuous: bool, evalf: Optional[bool] = False, theta: Optional[float] = 0.5, varsymbol: Optional[str] = 'u', verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0)", + "name": "_evaluate_impl", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 56, + "signature": "(expr, coords, coord_sys = None, other_arguments = None, simplify = False, verbose = False, evalf = False, mode = 'default', data_layout = None, check_extrapolated = False, smoothing = 1e-06, rbf = None, force_l2 = None)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "expr", + "type_hint": null, "default": null, "description": "" }, { - "name": "psi_fn", - "type_hint": "Union[uw.discretisation.MeshVariable, sympy.Basic]", + "name": "coords", + "type_hint": null, "default": null, "description": "" }, { - "name": "vtype", - "type_hint": "uw.VarType", - "default": null, + "name": "coord_sys", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": null, + "name": "other_arguments", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "continuous", - "type_hint": "bool", - "default": null, + "name": "simplify", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" }, { "name": "evalf", - "type_hint": "Optional[bool]", + "type_hint": null, "default": "False", "description": "" }, { - "name": "theta", - "type_hint": "Optional[float]", - "default": "0.5", + "name": "mode", + "type_hint": null, + "default": "'default'", "description": "" }, { - "name": "varsymbol", - "type_hint": "Optional[str]", - "default": "'u'", + "name": "data_layout", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", + "name": "check_extrapolated", + "type_hint": null, "default": "False", "description": "" }, { - "name": "bcs", + "name": "smoothing", "type_hint": null, - "default": "[]", + "default": "1e-06", "description": "" }, { - "name": "order", + "name": "rbf", "type_hint": null, - "default": "1", + "default": "None", "description": "" }, { - "name": "smoothing", + "name": "force_l2", "type_hint": null, - "default": "0.0", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Evaluate expression at coordinates with automatic unit handling.\n\nInternal implementation of :func:`evaluate`. The public wrapper\ndelegates here and then optionally applies the ``monotone``\nbounded-interpolation post-process. This body is unchanged from the\nhistorical ``evaluate`` so that ``monotone=False`` is bit-identical.\n\nThis function wraps the Cython evaluate_nd implementation to automatically\nhandle unit conversions and return unit-aware results.\n\nParameters\n----------\nexpr : sympy expression or UWexpression\n Expression to evaluate\ncoords : numpy.ndarray or UnitAwareArray\n Coordinates at which to evaluate. Supported forms:\n - numpy array of doubles (shape: n_points x n_dims) in model\n (non-dimensional) units\n - UnitAwareArray with dimensional coordinates (e.g., from mesh.X.coords)\n - Both work transparently - dimensional coords are auto-converted\n\n Python lists/tuples of coordinates \u2014 including quantity-valued\n lists such as ``[(x_qty, y_qty)]`` \u2014 are NOT supported and raise\n ``TypeError`` (the coordinate-units family is unsupported;\n maintainer ruling D7, 2026-07). Convert dimensional coordinates\n explicitly, e.g. ``float(uw.non_dimensionalise(x_qty))``, and\n pass a plain array.\ncoord_sys : mesh.N vector coordinate system, optional\n Coordinate system to use (default: None)\nother_arguments : dict, optional\n Additional arguments for evaluation (default: None)\nsimplify : bool, optional\n Whether to simplify expression (default: True)\nverbose : bool, optional\n Verbose output (default: False)\nevalf : bool, optional\n Force numerical evaluation via sympy evalf (default: False)\nmode : str, optional\n Evaluation mode controlling accuracy vs speed tradeoff.\n Options: ``\"default\"`` (accurate, projection for derivatives),\n ``\"fast\"`` (Clement gradient, RBF everywhere),\n ``\"projection\"`` (always L2 projection).\n Default: ``\"default\"``\ndata_layout : callable, optional\n Data layout specification (default: None)\ncheck_extrapolated : bool, optional\n Check for extrapolated values (default: False)\nsmoothing : float, optional\n Smoothing parameter for L2 projection (dimensionless).\n Only used when projection is active. Default: 1e-6\nrbf : bool, optional\n Expert override: Force RBF interpolation everywhere. Overrides mode.\nforce_l2 : bool, optional\n Expert override: Force L2 projection path. Overrides mode.\n\nReturns\n-------\nUWQuantity, UnitAwareArray, or ndarray\n If non-dimensional scaling is active, returns plain ndarray.\n If expression has units, returns UWQuantity (scalar) or UnitAwareArray.\n Otherwise returns plain ndarray.\n\nNotes\n-----\n**Evaluation Modes:**\n\n- ``\"fast\"``: Clement gradient (no solve), direct calculation, RBF everywhere\n- ``\"default\"``: Projection for derivatives (solve), direct otherwise, DMInterp + RBF\n- ``\"projection\"``: Always use L2 projection (solve), DMInterp + RBF\n\nThe `rbf` and `force_l2` parameters are expert overrides that take\nprecedence over the mode setting when explicitly provided.\n\nExamples\n--------\n>>> # Works with both dimensional and non-dimensional coords\n>>> result = uw.function.evaluate(T.sym, T.coords) # dimensional coords\n>>> result = uw.function.evaluate(T.sym, mesh.data[:, :2]) # non-dimensional\n>>> if hasattr(result, 'to'):\n... result_K = result.to('K') # Unit conversion", "harvested_comments": [ - "sympy function or mesh variable", - "meshVariables are required for:", - "u(t) - evaluation of u_fn at the current time", - "u*(t) - u_* evaluated from", - "psi is evaluated/stored at `order` timesteps. We can't" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "Expert overrides (override mode settings)", + "Works with both dimensional and non-dimensional coords", + "dimensional coords", + "non-dimensional", + "Unit conversion" ], - "parent_class": "Eulerian", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 552, - "signature": "(self, mesh: uw.discretisation.Mesh, psi_fn: sympy.Function, V_fn: sympy.Function, vtype: uw.VarType, degree: int, continuous: bool, swarm_degree: Optional[int] = None, swarm_continuous: Optional[bool] = None, varsymbol: Optional[str] = None, verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0, preserve_moments = False)", + "name": "_global_evaluate_impl", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 410, + "signature": "(expr, coords = None, coord_sys = None, other_arguments = None, simplify = False, verbose = False, evalf = False, mode = 'default', data_layout = None, check_extrapolated = False, smoothing = 1e-06, rbf = None, force_l2 = None, local_fallback = True)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "expr", + "type_hint": null, "default": null, "description": "" }, { - "name": "psi_fn", - "type_hint": "sympy.Function", - "default": null, + "name": "coords", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "V_fn", - "type_hint": "sympy.Function", - "default": null, + "name": "coord_sys", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "vtype", - "type_hint": "uw.VarType", - "default": null, + "name": "other_arguments", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": null, + "name": "simplify", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "continuous", - "type_hint": "bool", - "default": null, + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "swarm_degree", - "type_hint": "Optional[int]", - "default": "None", + "name": "evalf", + "type_hint": null, + "default": "False", "description": "" }, { - "name": "swarm_continuous", - "type_hint": "Optional[bool]", - "default": "None", + "name": "mode", + "type_hint": null, + "default": "'default'", "description": "" }, { - "name": "varsymbol", - "type_hint": "Optional[str]", + "name": "data_layout", + "type_hint": null, "default": "None", "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", + "name": "check_extrapolated", + "type_hint": null, "default": "False", "description": "" }, { - "name": "bcs", + "name": "smoothing", "type_hint": null, - "default": "[]", + "default": "1e-06", "description": "" }, { - "name": "order", + "name": "rbf", "type_hint": null, - "default": "1", + "default": "None", "description": "" }, { - "name": "smoothing", + "name": "force_l2", "type_hint": null, - "default": "0.0", + "default": "None", "description": "" }, { - "name": "preserve_moments", + "name": "local_fallback", "type_hint": null, - "default": "False", + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Global evaluate with automatic unit-aware results.\n\nInternal implementation of :func:`global_evaluate`. The public\nwrapper delegates here and then optionally applies the ``monotone``\nbounded-interpolation post-process. This body is unchanged from the\nhistorical ``global_evaluate`` so that ``monotone=False`` is\nbit-identical.\n\nSimilar to evaluate() but performs global evaluation across all processes.\nReturns unit-aware objects when expression has units.\n\nParameters\n----------\nexpr : sympy expression or UWexpression\n Expression to evaluate\ncoords : array-like, optional\n Coordinates at which to evaluate (default: None)\ncoord_sys : CoordinateSystem, optional\n Coordinate system to use (default: None)\nother_arguments : dict, optional\n Additional arguments for evaluation (default: None)\nsimplify : bool, optional\n Whether to simplify expression (default: True)\nverbose : bool, optional\n Verbose output (default: False)\nevalf : bool, optional\n Force numerical evaluation via sympy evalf (default: False)\nmode : str, optional\n Evaluation mode controlling accuracy vs speed tradeoff.\n Options: ``\"default\"`` (accurate, projection for derivatives),\n ``\"fast\"`` (Clement gradient, RBF everywhere),\n ``\"projection\"`` (always L2 projection).\n Default: ``\"default\"``\ndata_layout : callable, optional\n Data layout specification (default: None)\ncheck_extrapolated : bool, optional\n Check for extrapolated values (default: False)\nsmoothing : float, optional\n Smoothing parameter for L2 projection (dimensionless).\n Only used when projection is active. Default: 1e-6\nrbf : bool, optional\n Expert override: Force RBF interpolation everywhere. Overrides mode.\nforce_l2 : bool, optional\n Expert override: Force L2 projection path. Overrides mode.\nlocal_fallback : bool, optional\n Parallel-only. When True (default), query points that no rank can\n locate in-cell are resolved by a best-claim reduction so the parallel\n result matches serial ``evaluate`` (extrapolate from the true nearest\n cell). Set False to restore the legacy silently-wrong out-of-domain\n behaviour. The ``GE_LOCAL_FALLBACK`` env var, if set, overrides this.\n\nReturns\n-------\nUWQuantity, UnitAwareArray, or ndarray\n If non-dimensional scaling is active, returns plain ndarray.\n Otherwise returns result with appropriate unit tracking.\n\nNotes\n-----\nSee :func:`evaluate` for details on evaluation modes.", "harvested_comments": [ - "meshVariables are required for:", - "u(t) - evaluation of u_fn at the current time", - "u*(t) - u_* evaluated from", - "psi is evaluated/stored at `order` timesteps. We can't", - "be sure if psi is a meshVariable or a function to be evaluated" + "Expert overrides (override mode settings)", + "Map mode to internal flags (rbf, force_l2)", + "Expert overrides take precedence when explicitly provided", + "Use mode settings", + "Expert overrides - use provided values or defaults" ], - "status": "none", + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_normalize_monotone", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 656, + "signature": "(monotone)", + "parameters": [ + { + "name": "monotone", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Map the ``monotone`` kwarg to a canonical mode string or ``None``.\n\n``False`` / ``None`` \u2192 ``None`` (no limiting); ``True`` \u2192 ``\"clamp\"``\n(the cheap, always-safe choice); ``\"clamp\"`` / ``\"pick\"`` pass\nthrough. Anything else raises ``ValueError``.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SemiLagrangian", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1167, - "signature": "(self, mesh: uw.discretisation.Mesh, psi_fn: sympy.Function, V_fn: sympy.Function, vtype: uw.VarType, degree: int, continuous: bool, varsymbol: Optional[str] = 'u', verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0, fill_param = 3)", + "name": "_apply_monotone_limit", + "kind": "function", + "file": "src/underworld3/function/functions_unit_system.py", + "line": 675, + "signature": "(expr, coords, value, mode, coord_sys = None, other_arguments = None)", "parameters": [ { - "name": "mesh", - "type_hint": "uw.discretisation.Mesh", + "name": "expr", + "type_hint": null, "default": null, "description": "" }, { - "name": "psi_fn", - "type_hint": "sympy.Function", + "name": "coords", + "type_hint": null, "default": null, "description": "" }, { - "name": "V_fn", - "type_hint": "sympy.Function", + "name": "value", + "type_hint": null, "default": null, "description": "" }, { - "name": "vtype", - "type_hint": "uw.VarType", + "name": "mode", + "type_hint": null, "default": null, "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": null, + "name": "coord_sys", + "type_hint": null, + "default": "None", "description": "" }, { - "name": "continuous", - "type_hint": "bool", - "default": null, + "name": "other_arguments", + "type_hint": null, + "default": "None", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Bound an interpolated result to the local data range of its source\nfield (monotone / bounded interpolation).\n\nFor each evaluation coordinate this finds the ``mesh.dim + 1`` nearest\nsource-field DOFs and bounds ``value`` to their nodal min/max:\n\n- ``mode == \"clamp\"`` clips the result into ``[nbr_min, nbr_max]``\n (cheap, always bounded).\n- ``mode == \"pick\"`` keeps the in-bounds result and re-evaluates only\n the out-of-bounds subset via the bounded RBF path (``evalf=True``,\n which is intrinsically neighbour-bounded). ``evalf=True`` is a\n deliberate override -- the FE result is exactly what overshot, so we\n do *not* honour the caller's ``mode``/``rbf``/``force_l2`` here.\n ``coord_sys`` / ``other_arguments`` are propagated so the re-eval\n stays in the same frame.\n\nMVP scope: a single source MeshVariable. ``expr`` must resolve\ndirectly to one mesh variable (or one of its components, e.g.\n``T.sym``); composite / multi-field expressions raise ``ValueError``\nbecause the neighbour bound across fields is ill-defined.\n\nThe clamp algorithm is lifted verbatim from the SL trace-back limiter\nso that routing the DDt through here is bit-identical.", + "harvested_comments": [ + "\"pick\" re-evaluates the out-of-bounds subset via a collective", + "global_evaluate, gated on a rank-local mask -> would deadlock in", + "parallel (see TODO(parallel) below). Fail fast with a clear message", + "instead of hanging. \"clamp\" is rank-local-only and parallel-safe.", + "use the full var.data (matches the SL limiter)" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_evaluate_gradient_interpolant", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 124, + "signature": "(scalar_var, coords, component = None)", + "parameters": [ { - "name": "varsymbol", - "type_hint": "Optional[str]", - "default": "'u'", + "name": "scalar_var", + "type_hint": null, + "default": null, "description": "" }, { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "coords", + "type_hint": null, + "default": null, "description": "" }, { - "name": "bcs", + "name": "component", "type_hint": null, - "default": "[]", + "default": "None", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Evaluate gradient via Clement interpolant (fast, O(h) accurate).\n\nUses PETSc's DMPlexComputeGradientClementInterpolant which averages\ncell-wise gradients at vertices. A scratch DM is used internally to\navoid polluting the mesh's DM.\n\nParameters\n----------\nscalar_var : MeshVariable\n Field to compute gradient of.\ncoords : array-like\n Coordinates at which to evaluate gradient.\ncomponent : int or None\n For multi-component fields, which component.\n\nReturns\n-------\nndarray\n Gradient values, shape (n_points, dim).", + "harvested_comments": [ + "Handle multi-component fields", + "Convert coords to numpy array if needed", + "Get vertex coordinates (P1 node locations)", + "Check if field is P1 scalar - can use data directly", + "Create scratch DM with P1 scalar field for Clement computation" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_evaluate_gradient_projection", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 271, + "signature": "(scalar_var, coords, component = None)", + "parameters": [ { - "name": "order", + "name": "scalar_var", "type_hint": null, - "default": "1", + "default": null, "description": "" }, { - "name": "smoothing", + "name": "coords", "type_hint": null, - "default": "0.0", + "default": null, "description": "" }, { - "name": "fill_param", + "name": "component", "type_hint": null, - "default": "3", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Evaluate gradient via L2 projection (accurate, O(h\u00b2)).\n\nCreates a cached gradient MeshVariable and projector on the mesh.\nSubsequent calls reuse the cached objects and warm-start the solve\nfrom the previous solution.\n\nParameters\n----------\nscalar_var : MeshVariable\n Field to compute gradient of.\ncoords : array-like\n Coordinates at which to evaluate gradient.\ncomponent : int or None\n For multi-component fields, which component.\n\nReturns\n-------\nndarray\n Gradient values, shape (n_points, dim).", "harvested_comments": [ - "create a new swarm to manage here" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "Handle multi-component fields", + "Convert coords to numpy array if needed", + "Cache key based on variable and component", + "Initialize gradient cache on mesh if not present", + "Get or create cached projectors for each gradient component" ], - "parent_class": "Lagrangian", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1377, - "signature": "(self, swarm: uw.swarm.Swarm, psi_fn: sympy.Function, vtype: uw.VarType, degree: int, continuous: bool, varsymbol: Optional[str] = 'u', verbose: Optional[bool] = False, bcs = [], order = 1, smoothing = 0.0, step_averaging = 2)", + "name": "_compute_barycentric", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 379, + "signature": "(point, vertices)", "parameters": [ { - "name": "swarm", - "type_hint": "uw.swarm.Swarm", + "name": "point", + "type_hint": null, "default": null, "description": "" }, { - "name": "psi_fn", - "type_hint": "sympy.Function", + "name": "vertices", + "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Compute barycentric coordinates of a point within a simplex.\n\nParameters\n----------\npoint : ndarray\n Query point, shape (dim,).\nvertices : ndarray\n Simplex vertices, shape (dim+1, dim).\n\nReturns\n-------\nndarray\n Barycentric coordinates, shape (dim+1,).", + "harvested_comments": [ + "Not a simplex, return uniform weights", + "Build matrix T where columns are (v_i - v_n) for i = 0..dim-1", + "shape (dim, dim)", + "Solve T @ lambda = (point - v_n)", + "Last barycentric coordinate" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_evaluate_field_at_vertices", + "kind": "function", + "file": "src/underworld3/function/gradient_evaluation.py", + "line": 416, + "signature": "(var, component, mesh)", + "parameters": [ { - "name": "vtype", - "type_hint": "uw.VarType", + "name": "var", + "type_hint": null, "default": null, "description": "" }, { - "name": "degree", - "type_hint": "int", + "name": "component", + "type_hint": null, "default": null, "description": "" }, { - "name": "continuous", - "type_hint": "bool", + "name": "mesh", + "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Evaluate a field component at P1 vertex locations.\n\nFor P1 scalar fields, returns the data directly.\nFor higher-degree or multi-component fields, samples at vertex coordinates.\n\nParameters\n----------\nvar : MeshVariable\n The mesh variable to sample.\ncomponent : int or None\n Which component to evaluate (for multi-component fields).\nmesh : Mesh\n The mesh.\n\nReturns\n-------\nndarray\n Field values at vertices, shape (n_vertices,).", + "harvested_comments": [ + "Get vertex count", + "P1 scalar - return data directly", + "P1 multi-component - extract the component directly from data", + "Data layout: [v0_c0, v0_c1, ..., v1_c0, v1_c1, ...]", + "Reshape to (n_vertices, num_components) and extract component" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_expr_hash", + "kind": "function", + "file": "src/underworld3/function/pure_sympy_evaluator.py", + "line": 126, + "signature": "(expr)", + "parameters": [ { - "name": "varsymbol", - "type_hint": "Optional[str]", - "default": "'u'", + "name": "expr", + "type_hint": null, + "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Generate a hash for a sympy expression for caching.\n\nParameters\n----------\nexpr : sympy expression\n Expression to hash\n\nReturns\n-------\nstr\n Hash string", + "harvested_comments": [ + "sympy.Dummy carries a volatile, globally-unique dummy_index that", + "srepr embeds. The evaluate() coordinate-substitution path mints a", + "fresh Dummy per call, so an otherwise-identical expression would", + "hash differently every call and never hit the cache (issue #194).", + "Canonicalise dummies to name-stable Symbols before srepr. This only" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_is_offset_temperature", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 123, + "signature": "(pint_qty) -> bool", + "parameters": [ { - "name": "verbose", - "type_hint": "Optional[bool]", - "default": "False", + "name": "pint_qty", + "type_hint": null, + "default": null, "description": "" - }, + } + ], + "returns": "bool", + "existing_docstring": "True if ``pint_qty`` carries a non-multiplicative (offset)\ntemperature unit such as ``degC`` / ``degF``.\n\nThese are the units for which scalar multiplication (``value * unit``,\n``qty * 2``) is ambiguous in Pint and raises ``OffsetUnitCalculusError``\n\u2014 so they must not persist on a stored quantity. Detected via Pint's own\n``_is_multiplicative`` flag, which is False for offset units and True for\nabsolute kelvin/rankine *and* for a temperature difference\n(``delta_degC``, correctly left alone). The ``[temperature]`` guard keeps\nthe kelvin conversion at the call site well-defined.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "_from_pint", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 143, + "signature": "(cls, pint_qty, model_registry = None)", + "parameters": [ { - "name": "bcs", + "name": "pint_qty", "type_hint": null, - "default": "[]", + "default": null, "description": "" }, { - "name": "order", + "name": "model_registry", "type_hint": null, - "default": "1", + "default": "None", "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Create UWQuantity from a Pint Quantity object.\n\nThis is used by Model.to_model_units() and other internal methods\nthat work with Pint quantities directly.\n\nParameters\n----------\npint_qty : pint.Quantity\n A Pint Quantity object\nmodel_registry : pint.UnitRegistry, optional\n Model-specific registry (for model units)\n\nReturns\n-------\nUWQuantity\n New quantity with the Pint quantity's value and units", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "_compute_nd_value", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 204, + "signature": "(self) -> Union[float, np.ndarray]", + "parameters": [], + "returns": "Union[float, np.ndarray]", + "existing_docstring": "Compute the non-dimensional value using model scaling.", + "harvested_comments": [ + "If no units, value is already \"non-dimensional\"", + "Try to get scaling from model", + "Get the scale factor for our dimensionality", + "Extract scalar from scale (may be Pint Quantity)", + "Convert to base units first, then scale" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__add__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 329, + "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", + "parameters": [ { - "name": "smoothing", - "type_hint": null, - "default": "0.0", + "name": "other", + "type_hint": "Union['UWQuantity', float, int]", + "default": null, "description": "" - }, + } + ], + "returns": "'UWQuantity'", + "existing_docstring": "Addition via Pint.", + "harvested_comments": [ + "One or both dimensionless", + "Handle UWexpression", + "Delegate to UWexpression's __radd__", + "Handle SymPy expressions - LAZY EVALUATION approach", + "Wrap self in UWexpression first, then add symbolically" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__radd__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 373, + "signature": "(self, other)", + "parameters": [ { - "name": "step_averaging", + "name": "other", "type_hint": null, - "default": "2", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Right addition.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Lagrangian_Swarm", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "_apply_unit_aware_scaling", - "kind": "function", - "file": "src/underworld3/systems/solvers.py", - "line": 73, - "signature": "(dt_nondimensional, field, mesh)", + "name": "__sub__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 377, + "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", "parameters": [ { - "name": "dt_nondimensional", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "field", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "mesh", - "type_hint": null, + "name": "other", + "type_hint": "Union['UWQuantity', float, int]", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Helper function to apply unit-aware scaling to timestep estimates.\n\nDetects the units of the velocity field and applies appropriate time scaling\nto convert nondimensional timestep to physical time units.\n\nParameters\n----------\ndt_nondimensional : float or np.ndarray\n The nondimensional timestep estimate\nfield : MeshVariable or SymPy expression (often a Matrix)\n The velocity field - units are detected from this\nmesh : Mesh\n The mesh (may have reference to model with time scales)\n\nReturns\n-------\nfloat or UWQuantity\n Timestep with physical time units if detectable, otherwise nondimensional", + "returns": "'UWQuantity'", + "existing_docstring": "Subtraction via Pint.", "harvested_comments": [ - "Extract a component from field if it's a Matrix (common for velocity)", - "Extract first component: V_fn[0] or V_fn[0,0]", - "Try to get units from the field expression", - "Field has units - verify it has time dimension (as expected for velocity)", - "Get dimensionality: e.g., {'[length]': 1, '[time]': -1} for velocity" + "Handle UWexpression: UWQuantity - UWexpression \u2192 UWexpression", + "For subtraction, units must be compatible - convert other to self's units", + "Convert other's value to self's units", + "Units incompatible - use raw value (will be wrong but won't crash)", + "Result in self's units" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "__rsub__", "kind": "method", - "file": "src/underworld3/units.py", - "line": 57, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/function/quantities.py", + "line": 438, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Right subtraction: other - self.", + "harvested_comments": [ + "Handle UWexpression: UWexpression - UWQuantity is handled by UWexpression.__sub__", + "This handles: sympy.Basic - UWQuantity", + "LAZY EVALUATION approach (like __add__):", + "Wrap self in UWexpression first, preserving the full UWQuantity.", + "This ensures evaluate() knows the value has units and won't re-scale it." + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "_PintHelper", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "__mul__", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 166, - "signature": "(self, unknowns, material_name: str = None)", + "file": "src/underworld3/function/quantities.py", + "line": 470, + "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", "parameters": [ { - "name": "unknowns", - "type_hint": null, + "name": "other", + "type_hint": "Union['UWQuantity', float, int]", "default": null, "description": "" - }, - { - "name": "material_name", - "type_hint": "str", - "default": "None", - "description": "" } ], - "returns": null, - "existing_docstring": "Initialize a constitutive model.\n\nParameters\n----------\nunknowns : UnknownSet\n The solver's unknowns (velocity, pressure, etc.)\nmaterial_name : str, optional\n A distinguishing name for this material's symbols.\n If provided, symbols will be subscripted: \u03b7 \u2192 \u03b7_{name}\n Useful when bundling multiple models in MultiMaterialModel.", + "returns": "'UWQuantity'", + "existing_docstring": "Multiplication via Pint.", "harvested_comments": [ - "Define / identify the various properties in the class but leave", - "the implementation to child classes. The constitutive tensor is", - "defined as a template here, but should be instantiated via class", - "properties as required.", - "We provide a function that converts gradients / gradient history terms" + "Handle UnitAwareArray - Pint doesn't properly combine units with UnitAwareArray", + "We need to manually combine units and multiply values", + "Both have units - combine them via Pint", + "Multiply numeric values (extract numpy from UnitAwareArray)", + "Return UnitAwareArray with combined units" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "__rmul__", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2176, - "signature": "(self, unknowns, material_swarmVariable: IndexSwarmVariable, constitutive_models: list, normalize_levelsets: bool = False)", + "file": "src/underworld3/function/quantities.py", + "line": 593, + "signature": "(self, other)", "parameters": [ { - "name": "unknowns", + "name": "other", "type_hint": null, "default": null, "description": "" - }, - { - "name": "material_swarmVariable", - "type_hint": "IndexSwarmVariable", - "default": null, - "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Right multiplication.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__truediv__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 597, + "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", + "parameters": [ { - "name": "constitutive_models", - "type_hint": "list", + "name": "other", + "type_hint": "Union['UWQuantity', float, int]", "default": null, "description": "" - }, - { - "name": "normalize_levelsets", - "type_hint": "bool", - "default": "False", - "description": "" } ], - "returns": null, - "existing_docstring": "Parameters\n----------\nunknowns : UnknownSet\n The solver's authoritative unknowns (:math:`\\mathbf{u}`,\n :math:`D\\mathbf{F}/Dt`, :math:`D\\mathbf{u}/Dt`).\nmaterial_swarmVariable : IndexSwarmVariable\n Index variable tracking material distribution on particles.\nconstitutive_models : list of Constitutive_Model\n Pre-configured constitutive models for each material.\nnormalize_levelsets : bool, optional\n Whether to normalize level-set functions to enforce partition of unity.\n Set to True if IndexSwarmVariable does not maintain partition of unity.\n Default: False (assumes IndexSwarmVariable maintains partition of unity)", + "returns": "'UWQuantity'", + "existing_docstring": "Division via Pint.", "harvested_comments": [ - "Validate compatibility before initialization", - "Ensure model count matches material indices", - "CRITICAL: Share solver's unknowns with all constituent models", - "Composite model doesn't have its own material_name - constituents do" + "Check if other is a UWexpression - handle specially to preserve units", + "Both have units - compute combined units (self / other)", + "Only self has units", + "Only other has units - result has 1/other_units", + "Neither has units - just delegate to SymPy" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "MultiMaterialConstitutiveModel", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "__rtruediv__", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 529, - "signature": "(self, coordinate_system)", + "file": "src/underworld3/function/quantities.py", + "line": 673, + "signature": "(self, other)", "parameters": [ { - "name": "coordinate_system", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Initialize geographic coordinate accessor.\n\nParameters\n----------\ncoordinate_system : CoordinateSystem\n The coordinate system object with GEOGRAPHIC type", + "existing_docstring": "Right division: other / self.\n\nLAZY EVALUATION approach (same as __rsub__ and __add__):\nWrap self in UWexpression first, preserving the full UWQuantity.\nThis ensures evaluate() knows the value has units and won't re-scale it.\n\nRED FLAG: Never embed bare numbers (self._value) in symbolic expressions!\nBare numbers get re-dimensionalized during evaluation, causing unit bugs.", "harvested_comments": [ - "Cache for coordinate arrays", - "Set by _compute_coordinates" + "Handle SymPy types - use LAZY EVALUATION", + "LAZY EVALUATION approach:", + "Wrap self in UWexpression first, preserving the full UWQuantity.", + "This ensures evaluate() knows the value has units and won't re-scale it.", + "Store the full UWQuantity - Transparent Container" ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "__pow__", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 989, - "signature": "(self, coordinate_system)", + "file": "src/underworld3/function/quantities.py", + "line": 710, + "signature": "(self, exponent: Union[float, int]) -> 'UWQuantity'", "parameters": [ { - "name": "coordinate_system", - "type_hint": null, + "name": "exponent", + "type_hint": "Union[float, int]", "default": null, "description": "" } ], + "returns": "'UWQuantity'", + "existing_docstring": "Exponentiation via Pint.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__neg__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 718, + "signature": "(self) -> 'UWQuantity'", + "parameters": [], + "returns": "'UWQuantity'", + "existing_docstring": "Negation.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "_sympy_", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 772, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Initialize spherical/polar coordinate accessor.\n\nParameters\n----------\ncoordinate_system : CoordinateSystem\n The coordinate system object with SPHERICAL or CYLINDRICAL2D type", + "existing_docstring": "SymPy protocol - controls how SymPy converts this object.\n\nFor quantities WITH units: raise SympifyError to force SymPy to\nreturn NotImplemented, which triggers our __rmul__/__radd__ etc.\n\nFor quantities WITHOUT units: return the numeric value.", "harvested_comments": [ - "2 for polar, 3 for spherical", - "Cache for coordinate arrays" + "If we have units, don't let SymPy consume us silently", + "This forces SymPy to return NotImplemented so our __rmul__ gets called", + "No units - safe to return numeric value" ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "SphericalCoordinateAccessor", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "__float__", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 156, - "signature": "(self, varname = None, mesh = None, num_components = None, vtype = None, degree = 1, continuous = True, varsymbol = None, _register = True, units = None, units_backend = None)", + "file": "src/underworld3/function/quantities.py", + "line": 796, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Convert to float.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__str__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 808, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "String representation matching UWexpression style: value [units].", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 814, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "User-friendly representation matching UWexpression style: value [units].", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "__format__", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 820, + "signature": "(self, format_spec: str) -> str", "parameters": [ { - "name": "varname", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "mesh", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "num_components", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "vtype", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": null, - "default": "1", - "description": "" - }, - { - "name": "continuous", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "varsymbol", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "_register", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "units", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "units_backend", - "type_hint": null, - "default": "None", + "name": "format_spec", + "type_hint": "str", + "default": null, "description": "" } ], + "returns": "str", + "existing_docstring": "Formatted representation matching UWexpression style.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "_repr_latex_", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 835, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Initialize MeshVariable (only called for NEW objects).\n\nRetrieves initialization parameters from __new__ and handles DM reconstruction.", + "existing_docstring": "LaTeX representation for Jupyter notebooks.", "harvested_comments": [ - "Only initialize if this is a new object (not returned existing)", - "Already initialized", - "Get parameters - either from __new__ (via _init_params) or direct arguments", - "Parameters from __new__ method", - "Direct initialization (should not happen with __new__ pattern, but for safety)" + "Format value for LaTeX", + "Use scientific notation for very small/large numbers", + "Format units for LaTeX" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", + "name": "_repr_mimebundle_", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 90, - "signature": "(self, varname: Union[str, list], mesh: 'Mesh', num_components: Union[int, tuple] = None, vtype: Optional['uw.VarType'] = None, degree: int = 1, continuous: bool = True, varsymbol: Union[str, list] = None, persistent: bool = False, units: Optional[str] = None, units_backend: Optional[str] = None, **kwargs)", + "file": "src/underworld3/function/quantities.py", + "line": 856, + "signature": "(self, **kwargs)", "parameters": [ - { - "name": "varname", - "type_hint": "Union[str, list]", - "default": null, - "description": "" - }, - { - "name": "mesh", - "type_hint": "'Mesh'", - "default": null, - "description": "" - }, - { - "name": "num_components", - "type_hint": "Union[int, tuple]", - "default": "None", - "description": "" - }, - { - "name": "vtype", - "type_hint": "Optional['uw.VarType']", - "default": "None", - "description": "" - }, - { - "name": "degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "continuous", - "type_hint": "bool", - "default": "True", - "description": "" - }, - { - "name": "varsymbol", - "type_hint": "Union[str, list]", - "default": "None", - "description": "" - }, - { - "name": "persistent", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "units", - "type_hint": "Optional[str]", - "default": "None", - "description": "" - }, - { - "name": "units_backend", - "type_hint": "Optional[str]", - "default": "None", - "description": "" - }, { "name": "**kwargs", "type_hint": null, @@ -32491,2087 +57353,2490 @@ } ], "returns": null, - "existing_docstring": "Initialize enhanced mesh variable.\n\nArgs:\n varname: Variable name\n mesh: Mesh object\n num_components: Number of components (1=scalar, 2/3=vector, etc.)\n vtype: Variable type (optional)\n degree: Polynomial degree\n continuous: Whether variable is continuous\n varsymbol: Symbol for LaTeX representation\n persistent: Whether to enable persistence features\n units: Units for this variable (optional)\n units_backend: Units backend to use ('pint' or 'sympy')\n **kwargs: Additional arguments passed to base MeshVariable", + "existing_docstring": "MIME bundle for Jupyter display - highest priority representation.\n\nThis method has ABSOLUTE HIGHEST PRIORITY in Jupyter's display system.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "UWQuantity", + "is_public": false + }, + { + "name": "_ipython_display_", + "kind": "method", + "file": "src/underworld3/function/quantities.py", + "line": 867, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "IPython/Jupyter display hook - ABSOLUTE highest priority.\n\nShows the quantity with units in LaTeX format.", "harvested_comments": [ - "Check if already initialized (to prevent duplicate initialization)", - "Store configuration FIRST (before any method calls)", - "Weak reference to avoid circular deps", - "Create base variable without registration (we handle registration ourselves)", - "Never let base variable register itself" + "IPython not available - silent fallback" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "UWQuantity", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 53, - "signature": "(self, value: Union[float, int, np.ndarray], units: Optional[str] = None)", + "name": "_extract_value", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 15, + "signature": "(value, target_units = None)", "parameters": [ { "name": "value", - "type_hint": "Union[float, int, np.ndarray]", + "type_hint": null, "default": null, "description": "" }, { - "name": "units", - "type_hint": "Optional[str]", + "name": "target_units", + "type_hint": null, "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Initialize a UWQuantity.\n\nParameters\n----------\nvalue : float, int, or array-like\n The dimensional value\nunits : str or Pint Unit, optional\n Units specification", + "existing_docstring": "Universal helper to extract numeric value from quantities or pass through numbers.\n\nThis is a lightweight, zero-side-effect function that makes APIs accept both\nplain numbers and unit-aware quantities transparently.\n\nParameters\n----------\nvalue : float, int, Quantity, UWQuantity, tuple, list, or None\n Value to extract. Can be:\n - Plain number \u2192 returned as-is\n - Pint Quantity \u2192 magnitude extracted\n - UWQuantity \u2192 value extracted\n - tuple/list \u2192 recursively processed\n - None \u2192 returned as-is\ntarget_units : str, optional\n If provided, converts quantity to these units before extracting value.\n Ignored if value has no units.\n\nReturns\n-------\nfloat, int, tuple, list, or None\n Plain numeric value(s), ready to use in numeric APIs\n\nExamples\n--------\n>>> # Plain numbers pass through unchanged\n>>> _extract_value(5.0)\n5.0\n\n>>> # Quantities have magnitude extracted\n>>> _extract_value(5.0 * uw.units.km)\n5000.0 # in meters\n\n>>> # With target units\n>>> _extract_value(5.0 * uw.units.km, 'cm')\n500000.0\n\n>>> # Tuples/lists processed recursively\n>>> _extract_value((0.0, 5.0 * uw.units.km))\n(0.0, 5000.0)\n\n>>> # UWQuantity support\n>>> _extract_value(uw.quantity(5.0, \"km\"))\n5000.0", "harvested_comments": [ - "Store value as numpy array or scalar", - "Scalar - store directly", - "Handle units", - "Accept both strings and Pint Unit objects", - "Already a Pint Unit" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" + "Plain numbers pass through unchanged", + "Quantities have magnitude extracted", + "With target units", + "Tuples/lists processed recursively", + "UWQuantity support" ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/kdtree.py", - "line": 38, - "signature": "(self, data, leafsize = 16, **kwargs)", + "name": "_convert_coords_to_si", + "kind": "function", + "file": "src/underworld3/function/unit_conversion.py", + "line": 1008, + "signature": "(coords)", "parameters": [ { - "name": "data", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "leafsize", - "type_hint": null, - "default": "16", - "description": "" - }, - { - "name": "**kwargs", + "name": "coords", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Construct KD-tree from coordinate data.\n\nParameters\n----------\ndata : array-like\n Coordinate data to build tree from. Can be unit-aware (UnitAwareArray)\n or plain numpy array. Shape should be (n_points, n_dimensions).\nleafsize : int, optional\n Number of points at which to switch to brute-force (default 16)\n**kwargs : dict\n Additional arguments passed to pykdtree.KDTree", + "existing_docstring": "Convert coordinate input to numpy array in model coordinates.\n\nThis function handles unit-aware coordinates by converting them to model units,\nnot SI units. This ensures coordinates work correctly with meshes that use\nreference quantities for scaling.\n\nAccepts:\n- numpy arrays (assumed to be in model coordinates if no units)\n- lists/tuples of coordinates (each coordinate can be UWQuantity, Pint Quantity, or float/int)\n- lists/tuples of tuples (for multiple points)\n\nReturns numpy array of shape (n_points, n_dims) with dtype=np.float64 in model coordinates.", "harvested_comments": [ - "Import here to avoid circular imports", - "Check if data has units and store them", - "Extract raw numpy array for pykdtree (it doesn't understand units)", - "Get underlying numpy array without unit wrapping", - "Call parent constructor with raw data" + "Get the model for unit conversion", + "Helper function to convert a single coordinate value", + "Unit-aware coordinate - convert to model units", + "Extract the magnitude", + "Conversion returned plain number - use it" ], "status": "partial", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "KDTree", + "parent_class": null, "is_public": false }, { - "name": "__init__", + "name": "_notify_callbacks", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 96, - "signature": "(self, name: str, points: np.ndarray = None)", + "file": "src/underworld3/materials.py", + "line": 304, + "signature": "(self, event_type: str, *args)", "parameters": [ { - "name": "name", + "name": "event_type", "type_hint": "str", "default": null, "description": "" }, { - "name": "points", - "type_hint": "np.ndarray", - "default": "None", + "name": "*args", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create a fault surface.\n\nArgs:\n name: Identifier for this fault segment\n points: (N, 3) array of 3D points defining the fault surface.\n If None, the fault is empty and must be loaded or\n have points added later.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "FaultSurface", - "is_public": false - }, - { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 403, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Create an empty fault collection.", + "existing_docstring": "Notify all callbacks of a material change", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "FaultCollection", + "parent_class": "MaterialRegistry", "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 98, - "signature": "(self, name: str, surface: 'Surface', size: int = 1, proxy_degree: int = 1, existing: bool = False, units: Optional[str] = None, mask_width: Optional[float] = None, mask_profile: str = 'gaussian')", + "name": "_rank2_to_unscaled_matrix", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 91, + "signature": "(v_ij, dim, covariant = True)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "v_ij", + "type_hint": null, "default": null, "description": "" }, { - "name": "surface", - "type_hint": "'Surface'", + "name": "dim", + "type_hint": null, "default": null, "description": "" }, { - "name": "size", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "proxy_degree", - "type_hint": "int", - "default": "1", - "description": "" - }, - { - "name": "existing", - "type_hint": "bool", - "default": "False", - "description": "" - }, - { - "name": "units", - "type_hint": "Optional[str]", - "default": "None", - "description": "" - }, - { - "name": "mask_width", - "type_hint": "Optional[float]", - "default": "None", - "description": "" - }, - { - "name": "mask_profile", - "type_hint": "str", - "default": "'gaussian'", + "name": "covariant", + "type_hint": null, + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": "Create a variable on surface vertices.\n\nArgs:\n name: Variable name (key in pyvista point_data)\n surface: Parent Surface object\n size: Number of components per vertex (1 for scalar, 3 for vector)\n proxy_degree: Degree of proxy MeshVariable for .sym access\n existing: If True, wraps existing point_data (for loading from VTK)\n units: Optional units for this variable (e.g., \"Pa\", \"m/s\")\n mask_width: Width for distance-based mask (enables .mask property)\n mask_profile: Profile for mask function (\"step\", \"linear\", \"gaussian\", \"smoothstep\")", - "harvested_comments": [ - "Create array in pyvista's point_data (unless wrapping existing)", - "Proxy MeshVariable for .sym access (created lazily)" - ], - "status": "partial", + "existing_docstring": "Convert rank 2 tensor (v_ij) to voigt (vector) form (V_I)", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceVariable", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 355, - "signature": "(self, name: str, mesh: 'Mesh' = None, control_points: np.ndarray = None, symbol: str = None)", + "name": "_rank4_to_unscaled_matrix", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 112, + "signature": "(c_ijkl, dim)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "c_ijkl", + "type_hint": null, "default": null, "description": "" }, { - "name": "mesh", - "type_hint": "'Mesh'", - "default": "None", - "description": "" - }, - { - "name": "control_points", - "type_hint": "np.ndarray", - "default": "None", - "description": "" - }, - { - "name": "symbol", - "type_hint": "str", - "default": "None", + "name": "dim", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create a surface.\n\nArgs:\n name: Identifier for this surface\n mesh: Computational mesh (required for .sym access and distance field)\n control_points: (N, 3) array of 3D points defining the surface.\n If None, the surface is empty and must be loaded or\n have points set later.\n symbol: Short LaTeX-friendly symbol for math display (e.g., \"F\" for \"fault\").\n If None, defaults to first letter of name capitalized.\n Used in expressions like d_F instead of {surf_fault_distance}.", - "harvested_comments": [ - "Register with mesh for adaptation notifications", - "Math symbol for clean LaTeX display", - "Default: first letter capitalized (e.g., \"main_fault\" -> \"M\")", - "Extract first letter, capitalize", - "Level 1: Control points (primary for evolving surfaces)" - ], - "status": "partial", - "needs": [ - "NEEDS_RETURNS" - ], - "parent_class": "Surface", - "is_public": false - }, - { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1275, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Create an empty surface collection.", + "existing_docstring": "Convert rank 4 tensor (c_ijkl) to matrix form (C_IJ)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", + "parent_class": null, "is_public": false }, { - "name": "__init__", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 119, - "signature": "(self, name: Optional[str] = None, **kwargs)", + "name": "_unscaled_matrix_to_rank2", + "kind": "function", + "file": "src/underworld3/maths/tensors.py", + "line": 134, + "signature": "(V_I, dim)", "parameters": [ { - "name": "name", - "type_hint": "Optional[str]", - "default": "None", + "name": "V_I", + "type_hint": null, + "default": null, "description": "" }, { - "name": "**kwargs", + "name": "dim", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Initialize a new Model instance.\n\nParameters:\n-----------\nname : str, optional\n Human-readable name for this model instance\n**kwargs : dict\n Additional arguments for Pydantic BaseModel", + "existing_docstring": "Convert to rank 2 tensor (v_ij) from voigt (vector) form (V_I)", "harvested_comments": [ - "Handle name generation before calling super().__init__", - "Set initial state if not provided", - "Transition through initializing to configured", - "Auto-register as default model if no default exists", - "This ensures the first user-created model becomes default automatically" + "convert Voight form V_I to v_ij (matrix)" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_dm_stack_bcs", + "name": "_unscaled_matrix_to_rank4", "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 448, - "signature": "(dm, boundaries, stacked_bc_label_name)", + "file": "src/underworld3/maths/tensors.py", + "line": 155, + "signature": "(C_IJ, dim)", "parameters": [ { - "name": "dm", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "boundaries", + "name": "C_IJ", "type_hint": null, "default": null, "description": "" }, { - "name": "stacked_bc_label_name", + "name": "dim", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Convert to rank 4 tensor (c_ijkl) from matrix form (C_IJ)", "harvested_comments": [ - "Load this up on the stack" + "Wrap values that have __getitem__ (e.g. UWexpression from", + "MathematicalMixin) to prevent SymPy's _setter_iterable_check", + "from rejecting them during NDimArray assignment.", + "C_IJ -> C_ijkl -> C_jilk -> C_ij_lk -> C_jikl (Symmetry)" ], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], "parent_class": null, "is_public": false }, { - "name": "__cinit__", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 59, - "signature": "def __cinit__( self,\n points_input not None: numpy.ndarray ) :", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_is_radial_coords", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 48, + "signature": "(mesh) -> bool", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": null, - "is_public": false - }, - { - "name": "__dealloc__", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 81, - "signature": "def __dealloc__(self):", - "parameters": [], - "returns": null, - "existing_docstring": null, + "returns": "bool", + "existing_docstring": "True for coordinate systems with a radial boundary (the snap-back\ntarget is a fixed ``|r|``). Cartesian boundaries are flat \u2014 zeroing the\nnormal displacement keeps nodes on the face, so no snap-back is needed.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_convert_coords_to_tree_units", - "kind": "method", - "file": "src/underworld3/ckdtree.pyx", - "line": 92, - "signature": "def _convert_coords_to_tree_units(self, coords):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_auto_grad_smoothing_length", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 62, + "signature": "(mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": null, - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 483, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "The mesh's characteristic (uniform) cell size \u2014 mean edge length,\nparallel-safe \u2014 returned as a unit-aware length when the mesh carries\ncoordinate units, else a bare (non-dimensional) float. Used as the\ndefault ``grad_smoothing_length`` so gradient de-noising is on by\ndefault at a scale comparable to the grid (the validated production\nsetting); ``None`` turns it off.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 709, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_slip_normals", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 84, + "signature": "(mesh, boundary_coords: np.ndarray)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary_coords", + "type_hint": "np.ndarray", + "default": null, + "description": "" + } ], - "parent_class": "ViscousFlowModel", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 925, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Unit outward normals at ``boundary_coords`` from the projected\nboundary-normal field.\n\nRe-projects ``mesh._projected_normals`` (``mesh.Gamma_P1``) first so the\nnormals reflect the mesh's *current* coordinates \u2014 the projected field is\nstale after any deform. Returns ``(normals, valid)`` where ``normals`` is\n``(k, cdim)`` and ``valid`` is a boolean mask; ``valid`` is ``False`` for\nnodes with a degenerate (zero / non-finite) normal (e.g. box corners\nwhere opposing face normals cancel, or an occasional unlocatable vertex).\nSuch nodes should be pinned, not slipped.", "harvested_comments": [ - "# feedback on this instance" + "Projection unavailable / degenerate on this mesh \u2014 fall back to", + "all-pinned boundaries (valid stays all-False below)." ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoPlasticFlowModel", + "parent_class": null, "is_public": false }, { - "name": "_plastic_effective_viscosity", - "kind": "property", - "file": "src/underworld3/constitutive_models.py", - "line": 1279, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# First order ...", - ".rewrite(sympy.Piecewise)" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_ot_adapt_step", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 114, + "signature": "(mesh, field) -> bool", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "ViscoElasticPlasticFlowModel", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1479, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, + "returns": "bool", + "existing_docstring": "Run one OT-reset adapt event. Returns ``True`` if the mesh moved,\n``False`` if the skip-on-aligned check short-circuited.\n\nSee the module docstring for the algorithm. ``field`` is the scalar\nMeshVariable whose gradient drives refinement; it is always FE-remapped\nonto the adapted mesh. ``reference_coords`` overrides the reset target\nfor this call only (defaults to ``mesh._ot_adapt_reference_coords``).\n\n``grad_smoothing_length`` de-noises ``|\u2207field|`` before the metric is\nbuilt: ``\"auto\"`` (default) \u2248 the mesh's uniform cell size \u2014 the\nvalidated setting that keeps the metric clean at production refinement;\n``None`` turns it off; a number or Pint length sets it explicitly\n(user-supplied lengths are unit-aware via the projection's\nnon-dimensionalisation).", "harvested_comments": [ - "super()._object_viewer()", - "## Viscous deformation\"))", - "### Elastic deformation\"))", - "### Plastic deformation\"))", - "# Todo: add all the other properties in here" + "Resolve the gradient de-noising length: \"auto\" \u2248 uniform grid size.", + "R for the alignment clamp matches follow_metric: max(refine, coarsen).", + "For radial coordinate systems (where boundary slip is used), create the", + "projected-normal field up front \u2014 before the metric builder / OT mover", + "set up any solver DM. Creating that MeshVariable mid-mover would stale" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1645, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_boundary_facets", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 251, + "signature": "(mesh, cdim)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cdim", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "DiffusionModel", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1716, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Boundary facets + opposite cell-vertex, found from the cell topology.\n\nFor each cell, every facet (edge in 2D, triangle in 3D) is a candidate\nboundary facet; one that occurs in **exactly one** cell is on the\nboundary. Returns ``(facets, opp)`` where ``facets`` is ``(n_bnd, k)``\n(``k=2`` for 2D edges, ``k=3`` for 3D triangles) and ``opp`` is the\ncell vertex opposite each facet \u2014 used to orient the facet normal\noutward. Returns ``(None, None)`` for non-simplicial meshes.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "AnisotropicDiffusionModel", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1794, - "signature": "(self)", - "parameters": [], + "name": "_all_boundary_labels", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 309, + "signature": "(mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Named codim-1 boundary labels of the mesh, skipping the synthetic /\nnon-geometric ones (``All_Boundaries``, ``Null_Boundary``, and the\nAnnulus single-point ``Centre`` pseudo-label that hard-aborts PETSc).", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "GenericFluxModel", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1934, - "signature": "(self)", - "parameters": [], + "name": "_resolve_slip", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 326, + "signature": "(mesh, slip_spec)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "slip_spec", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Resolve the ``slip_spec`` (the value passed as ``boundary_slip`` /\n``slip_surfaces``) into a tuple of named slip-surface labels, and\npre-touch ``mesh.Gamma_P1`` so the projected-normal field ``_n_proj``\nexists BEFORE any mover builds its solver DM (creating that MeshVariable\nmid-mover would stale the DM handle \u2014 see project_uw3_smoother_footguns;\nthe matrix-free ``mmpde`` mover has no such DM but the elliptic /\nanisotropic movers do).\n\nAccepted forms (back-compatible):\n * ``True`` / truthy / legacy ``'ring'``,``'box'`` strings \u2192 ALL named\n codim-1 boundary surfaces slip.\n * ``False`` / ``None`` / ``[]`` \u2192 no slip (pin all boundaries).\n * a label name, or a list of label names \u2192 only those surfaces slip.\n * a ``dict`` ``{label: snap_bool}`` \u2192 those labels slip; ``snap_bool``\n is the per-surface return-to-bounds flag (``False`` = FREE surface,\n slip but do not snap back). The dict keys are the slip labels.\n\nReturns the tuple of slip-surface label names (possibly empty).", "harvested_comments": [ - "# feedback on this instance" + "a single explicit label name", + "an iterable of label names", + "Pre-create the projected-normal field (footgun-safe; see docstring)." ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "DarcyFlowModel", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2142, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" + "name": "_nearest_on_facets_2d", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 372, + "signature": "(pts, seg)", + "parameters": [ + { + "name": "pts", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "seg", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "none", + "returns": null, + "existing_docstring": "Closest point on a set of 2D line segments. ``pts`` (m,2),\n``seg`` (nf,2,2). Returns (m,2) closest points (over all segments).", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "TransverseIsotropicFlowModel", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2342, - "signature": "(self)", - "parameters": [], + "name": "_nearest_on_facets_3d", + "kind": "function", + "file": "src/underworld3/meshing/_ot_adapt.py", + "line": 388, + "signature": "(pts, tri)", + "parameters": [ + { + "name": "pts", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tri", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Closest point on a set of 3D triangles. ``pts`` (m,3),\n``tri`` (nf,3,3). Returns (m,3). Per-point loop, vectorised over\ntriangles via the standard region-based closest-point algorithm.", + "harvested_comments": [ + "interior barycentric point; clamp handles edge/vertex regions well", + "enough for a small return-to-bounds correction on convex surfaces." + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "MultiMaterialConstitutiveModel", + "parent_class": null, "is_public": false }, { - "name": "_reset", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 231, - "signature": "(self)", - "parameters": [], + "name": "_as_float", + "kind": "function", + "file": "src/underworld3/meshing/bounding_surface.py", + "line": 31, + "signature": "(x)", + "parameters": [ + { + "name": "x", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Coerce a radius/length that may be a UWQuantity / pint Quantity to a\nbare non-dimensional float in the mesh's coordinate units.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 248, - "signature": "(self)", + "name": "_require_pyvista", + "kind": "function", + "file": "src/underworld3/meshing/faults.py", + "line": 56, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Check pyvista availability with helpful error message.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Constitutive_Model", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", + "name": "__getitem__", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 335, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" + "file": "src/underworld3/meshing/faults.py", + "line": 449, + "signature": "(self, name: str) -> FaultSurface", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } ], - "status": "none", + "returns": "FaultSurface", + "existing_docstring": "Get a fault by name.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscousFlowModel", + "parent_class": "FaultCollection", "is_public": false }, { - "name": "_object_viewer", + "name": "__iter__", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 763, + "file": "src/underworld3/meshing/faults.py", + "line": 453, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance", - "# Todo: add all the other properties in here" - ], - "status": "none", + "existing_docstring": "Iterate over fault names.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoPlasticFlowModel", + "parent_class": "FaultCollection", "is_public": false }, { - "name": "_object_viewer", + "name": "__len__", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1390, + "file": "src/underworld3/meshing/faults.py", + "line": 457, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "## Viscous deformation\"))", - "# If elasticity is active:", - "### Elastic deformation\"))", - "If plasticity is active", - "### Plastic deformation\"))" - ], - "status": "none", + "existing_docstring": "Number of faults in collection.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "FaultCollection", "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1525, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" + "name": "_auto_pinned_labels", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 136, + "signature": "(mesh) -> tuple", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "none", + "returns": "tuple", + "existing_docstring": "All non-sentinel geometric boundary labels on the mesh.\n\nSkips ``All_Boundaries`` / ``Null_Boundary`` (sentinels) and\nknown non-geometric pressure-pin markers such as ``Centre`` on\nthe Annulus (a single-point marker whose underlying ``DMLabel``\nhas an invalid communicator and hard-crashes any\n``getNumValues`` / ``getValueIS`` / ``view`` call).", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "DiffusionModel", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1670, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" + "name": "_owned_vertex_mask", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 154, + "signature": "(dm)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "none", + "returns": null, + "existing_docstring": "Local-chart boolean mask: True for owned vertices, False for\nghosts (leaves of the point StarForest). Used by the parallel\ntests; the smoother itself derives ownership from the global\nsection attached to its scalar DM clone.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "TransverseIsotropicFlowModel", + "parent_class": null, "is_public": false }, { - "name": "__new__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 88, - "signature": "(cls, index, system, pretty_str = None, latex_str = None, mesh = None, axis_index = None)", + "name": "_pinned_mask", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 178, + "signature": "(dm, pinned_labels)", "parameters": [ { - "name": "index", + "name": "dm", "type_hint": null, "default": null, "description": "" }, { - "name": "system", + "name": "pinned_labels", "type_hint": null, "default": null, "description": "" - }, - { - "name": "pretty_str", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "latex_str", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "mesh", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "axis_index", - "type_hint": null, - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Local-chart boolean mask: True where the vertex belongs to (or\nis the endpoint of an edge in) any of ``pinned_labels``.\n\nUW3 mesh generators tag boundaries by EDGE rather than by\nvertex; the vertex stratum sometimes misses 1-2 endpoint\nvertices at the gmsh seam (e.g. \u03b8=0\u00b0/180\u00b0 on the Annulus outer\nrim). Pinning by vertex-stratum-only would leave those\n\"seam\" vertices free, and the smoother would pull them\ninward. Taking the closure of the tagged edges recovers them.\n\nTolerates labels that are present but empty (e.g. the\n``Centre`` pressure-pin marker on an Annulus, whose underlying\n``DMLabel`` has no strata and hard-crashes any query).", "harvested_comments": [ - "Create as a BaseScalar with the same index and system" + "Tagged vertex \u2014 pin directly.", + "Tagged edge \u2014 pin both endpoint vertices." ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWCoordinate", + "parent_class": null, "is_public": false }, { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 248, - "signature": "(self)", - "parameters": [], + "name": "_build_scalar_dm", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 228, + "signature": "(dm)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "A clone of the topological DM with a 1-dof-per-vertex local\nsection. Used to size the adjacency Mat and to produce the global\nvertex numbering.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWCoordinate", + "parent_class": null, "is_public": false }, { - "name": "__del__", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1237, - "signature": "(self)", - "parameters": [], + "name": "_build_adjacency_matrix", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 245, + "signature": "(mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Build the parallel vertex-vertex adjacency as a PETSc AIJ Mat.\n\nEach rank inserts entries for every locally-visible edge using\nGLOBAL vertex indices; ``mat.assemble()`` combines cross-rank\ncontributions, so that after assembly an owned-vertex row has\nevery neighbour it would in a serial run \u2014 even when the\nincident edge lives in a cell owned by another rank that is not\nin this rank's overlap.\n\nReturns\n-------\nA : PETSc.Mat\n Unweighted vertex-vertex adjacency, entries are 1.0 where an\n edge exists. Divide the result of ``A @ x`` by the degree\n vector to get the neighbour average.\ndm_scalar : PETSc.DMPlex\n Clone of ``mesh.dm`` with a 1-dof-per-vertex section. Owns\n the parallel layout for the Mat and any vectors of the same\n shape.\ngsection : PETSc.Section\n Global section of ``dm_scalar`` \u2014 the owned-vertex numbering.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": null, "is_public": false }, { - "name": "_build_kd_tree_index_DS", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2095, - "signature": "(self)", - "parameters": [], + "name": "_min_incident_edge", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 305, + "signature": "(dm, coords)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "Build this from the PETScDS rather than the SWARM", - "self._index.build_index()" + "existing_docstring": "Per-vertex minimum incident edge length (local-chart\nv-pStart order). Used as an optional secondary per-node cap on\nthe spring step (the primary tangle guard is the coherent global\nsigned-area backtrack in ``_winslow_spring``).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "none", + "parent_class": null, + "is_public": false + }, + { + "name": "_tri_cells", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 329, + "signature": "(dm)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Triangle vertex-index triples (local-chart, v-pStart order).\n\nReturns an ``(n_tri, 3)`` int array, or ``None`` if the mesh is\nnot all-triangle (then the global signed-area backtrack is\nskipped and only the optional per-node edge cap guards against\ntangling).", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": null, "is_public": false }, { - "name": "_build_kd_tree_index", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2118, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "def mesh_face_skeleton_kdtree(mesh):", - "Use raw internal array for KD-tree construction (avoid unit-aware wrapping)", - "for face in range(cell_num_faces):", - "points = self.dm.getTransitiveClosure(cell_faces[face])[0][", - "-face_num_points:" + "name": "_signed_areas", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 351, + "signature": "(coords, tris)", + "parameters": [ + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tris", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "none", + "returns": null, + "existing_docstring": "Signed area of each triangle (sign = orientation).", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": null, "is_public": false }, { - "name": "_build_kd_tree_index_PIC", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2200, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# Bootstrapping - the kd-tree is needed to build the index but", - "# the index is also used in the kd-tree.", - "Create a temp swarm which we'll use to populate particles", - "at gauss points. These will then be used as basis for", - "kd-tree indexing back to owning cells." + "name": "_edge_pairs", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 483, + "signature": "(dm)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "none", + "returns": null, + "existing_docstring": "``(n_edge, 2)`` int array of edge endpoint vertex indices in\nlocal-chart (v - pStart) order \u2014 the spring network's bars.\n\nSkips edges whose endpoints are not both in the local vertex\nstratum (rank-ghost incomplete edges); the spring path is\nserial-exact (see module docstring).", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": null, "is_public": false }, { - "name": "_get_domain_centroids", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2771, - "signature": "(self)", - "parameters": [], + "name": "_winslow_spring", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 506, + "signature": "(mesh, metric, pinned_labels, verbose, max_cg_iters = 300, boundary_slip = False, shape_w = 1.0, size_w = 8.0, n_sweeps = None)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "metric", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pinned_labels", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "max_cg_iters", + "type_hint": null, + "default": "300", + "description": "" + }, + { + "name": "boundary_slip", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "shape_w", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "size_w", + "type_hint": null, + "default": "8.0", + "description": "" + }, + { + "name": "n_sweeps", + "type_hint": null, + "default": "None", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Metric-driven mesh grading by elastic-spring equilibrium.\n\nEvery mesh edge is a linear spring whose *rest length* is set\nfrom the target density,\n\n.. math::\n\n L^0_{ij} \\;\\propto\\; \\rho_{\\mathrm{tgt}}^{-1/d},\n\nscaled once so the total rest length equals the total current\nedge length (overall scale preserved \u2014 pure redistribution).\nThe interior nodes are moved to the **mechanical equilibrium**\nby *minimising the truss energy*\n\n.. math::\n\n E(\\mathbf{x}) \\;=\\; \\tfrac12 \\sum_{e}\n \\big(\\,|\\mathbf{x}_i-\\mathbf{x}_j| - L^0_e\\,\\big)^2\n\nover the free (non-pinned) nodes with **nonlinear conjugate\ngradients** (Polak\u2013Ribi\u00e8re\u207a) and an Armijo line search whose\ntrial step is rejected if any cell would invert. Solving the\nequilibrium \u2014 rather than creeping with damped Jacobi sweeps,\nwhich stall against a per-sweep global tangle freeze \u2014 is what\nlets the absolute rest-length target actually grade the mesh\ntoward spacing ``\u221d \u03c1_tgt^{-1/d}``.\n\n``\u03c1_tgt`` is Lagrangian (``metric = f(r0)`` with ``r0`` a frozen\nmesh variable), so the rest lengths are fixed per material node\n(computed once) and the *design* grading is restored even after\nthe mesh deformed. Uniform ``\u03c1_tgt`` \u21d2 all rest lengths equal\nthe mean edge length \u21d2 only a benign mild regularisation toward\nuniform spacing (no grading change).\n\n``max_cg_iters`` caps the CG iterations (CG converges far faster\nthan the old Jacobi sweep budget); ``n_sweeps`` is its deprecated\nformer name, accepted for one cycle with a DeprecationWarning.\nThe old ``relax`` / ``step_frac`` parameters were unused on the\nequilibrium path (the CG line search controls the step and the\ninversion guard) and have been removed. ``n_iters`` / ``alpha``\ndo not apply.", + "harvested_comments": [ + "Boundary tangential slip via the mesh-owned contract", + "(boundary-slip-strategy.md): each slip vertex slides tangentially and", + "snaps back onto its bounding surface (radial ring / plane / facet);", + "non-slip, junction, and degenerate-normal vertices pin. Replaces the", + "per-ring COM radial snap (one node/ring anchored the rotation gauge);" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": null, "is_public": false }, { - "name": "_data_layout", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1299, - "signature": "(self, i, j = None)", + "name": "_use_direct_solver", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 848, + "signature": "(solver, singular = False, elliptic = True)", "parameters": [ { - "name": "i", + "name": "solver", "type_hint": null, "default": null, "description": "" }, { - "name": "j", + "name": "singular", "type_hint": null, - "default": "None", + "default": "False", + "description": "" + }, + { + "name": "elliptic", + "type_hint": null, + "default": "True", "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "_BaseMeshVariable", - "is_public": false - }, - { - "name": "_setup_ds", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1344, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, + "existing_docstring": "Force a cached MA sub-solver onto a sparse **direct** factorisation\n(MUMPS LU) instead of the UW3 default GMRES + GAMG.\n\n**Parallel safety (parallel-singular-corruption, 2026-05-27):** MUMPS\n(the only parallel LU in this build) corrupts the heap when the same\nfactorisation path is exercised over *repeated* solves at np >= 3 \u2014 a\nprobabilistic SEGV/SIGBUS or MPI deadlock (the UW3 default GMRES+GAMG, which\nnever calls MUMPS, is clean). Since the movers re-solve in a Picard/outer\nloop, MUMPS is unusable in parallel here. Under MPI this function therefore\nfalls back to the MUMPS-free iterative path (:func:`_use_iterative_solver`);\nthe direct MUMPS path below is kept for the validated **serial** efficiency\nlever. ``elliptic`` is forwarded to the iterative fallback (\u03c6-Poisson \u2192\nGAMG; mass systems \u2192 CG+Jacobi).\n\nWhy this is the dominant MA-efficiency lever (profiled 2026-05-17,\nres-16 Annulus, AMP=8, warm re-call): the Picard loop fixes the\nmesh, so the \u03c6-Poisson Laplacian and the Hessian-recovery SPD mass\nmatrix are *constant operators* re-solved ~40\u00d7 with only the RHS\nchanging. With GAMG, every ``solve()`` pays a full multigrid\n**setup** (the constant near-nullspace re-attach forces it) \u2014 the\nHessian solve alone was ~0.93 s/iter \u2248 37 s. These problems are\ntiny (\u227210\u2074 DOF); MUMPS factorises in milliseconds and the per-iter\ncost collapses to a back-substitution. A direct solve is also\n*exact* (machine precision, tighter than the GMRES rtol), so the\nPicard fixed point \u2014 hence the grading/quality \u2014 is unchanged.\n\n``singular=True`` (the pure-Neumann \u03c6 Poisson): MUMPS null-pivot\ndetection (ICNTL(24)=1) handles the rank-1-deficient operator; the\n``constant_nullspace`` hook still removes the constant mode from\nthe RHS/solution, so the result is the same consistent solution\nthe iterative path produced \u2014 but it also eliminates the\nGAMG-on-pure-Neumann ``DIVERGED_LINEAR_SOLVE`` re-solve pathology.", "harvested_comments": [ - "self.clean_name ## Filling up the options database", - "only active if discontinuous", - "Check if this is the first field or if we need to rebuild the DM", - "(needed to ensure Section is properly synchronized with field list)", - "DM already has fields - need to rebuild to sync Section" + "Parallel: MUMPS-repeated corrupts the heap (see docstring) \u2014 use the", + "MUMPS-free iterative path instead. Serial keeps the fast direct solve.", + "These three sub-problems are *linear* (\u03c6 Poisson with the Hessian", + "source frozen; the SPD Hessian-recovery mass system; the \u2207\u03c6", + "projection) \u2192 one KSP solve, no Newton line-search / 2nd iterate" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_set_vec", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1436, - "signature": "(self, available)", + "name": "_use_iterative_solver", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 933, + "signature": "(solver, singular = False, elliptic = True)", "parameters": [ { - "name": "available", + "name": "solver", "type_hint": null, "default": null, "description": "" + }, + { + "name": "singular", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "elliptic", + "type_hint": null, + "default": "True", + "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Parallel-scalable alternative to ``_use_direct_solver``: keep\nthe *same factor/setup-once-reuse pattern* (the real efficiency\nlever) but with an **iterative** PC so it scales beyond the\nserial / modest-size regime where sparse direct factorisation is\nviable (this PETSc build has only MUMPS + serial builtin LU \u2014 no\nhypre / SuperLU_DIST).\n\nThe Picard loop fixes the mesh \u21d2 the operator is constant across\nthe ~25 inner solves; ``snes_lag_jacobian=-2`` /\n``snes_lag_preconditioner=-2`` build the PC **once per\n``_winslow_elliptic`` call** and reuse it for every inner solve\n(the GAMG hierarchy / Jacobi diagonal is *not* rebuilt per\niteration \u2014 that per-iter GAMG re-setup was the original ~0.9 s\nHessian cost). ``_deform_mesh`` resets ``is_setup`` so the lag\ncounter resets and the PC is correctly rebuilt on the next call's\nfirst solve. Combined with a Krylov **warm start** from the\nprevious Picard \u03c6 (caller passes ``zero_init_guess=False``), the\ninner solves are a handful of CG iterations on an already-built\nhierarchy.\n\n``elliptic=True`` (the \u03c6-Poisson Laplacian): CG + GAMG with the\nconstant near-nullspace (already attached via\n``constant_nullspace`` \u2014 GAMG needs it for the pure-Neumann\noperator). ``elliptic=False`` (the SPD Hessian-recovery / \u2207\u03c6 mass\nsystems): a mass matrix is spectrally trivial \u2014 CG + Jacobi\nconverges in a few iterations with **no** hierarchy setup, fully\nparallel; GAMG there would be wasted setup.\n\nNumerics: an iterative solve to a tight ``ksp_rtol`` reproduces\nthe BFO Picard fixed point \u2014 hence the grading \u2014 to well within\nits 4-dp precision (validated against the direct path); it is a\n*cost/parallelism* change, not a formulation change.", "harvested_comments": [ - "not sure if required, but to be sure.", - "This is set for checkpointing." + "See _use_direct_solver: snes_max_it=1 stops a converged-reason", + "viewer mislabelling these linear ksponly sub-solves as", + "\"DIVERGED_MAX_IT iterations 0\". Numerically inert.", + "Krylov choice is per-operator (set in the branches below):", + "* elliptic \u03c6-Poisson \u2192 FGMRES. The UW3 DMPlex-FEM assembly +" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "__del__", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1530, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_patch_volumes", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 1047, + "signature": "(tris, coords, n_verts, vol_field = None)", + "parameters": [ + { + "name": "tris", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_verts", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "vol_field", + "type_hint": null, + "default": "None", + "description": "" + } ], - "parent_class": "_BaseMeshVariable", - "is_public": false - }, - { - "name": "__cinit__", - "kind": "method", - "file": "src/underworld3/function/_dminterp_wrapper.pyx", - "line": 71, - "signature": "def __cinit__(self):", - "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Per-vertex dual-patch area: a node's share (1/3) of every\nincident triangle's |area|. \u03c1_cur \u221d 1/patch for the (opt-in,\nn_outer>1) outer MA composition; at equidistribution\n``patch \u00b7 \u03c1_tgt`` is uniform.\n\nThis quantity is exactly the **lumped P1 mass diagonal** ``M_ii = \u222b N_i dV``.\nThe hand-rolled local sum below is serial-exact, but **under-counts shared\nvertices on rank-partition boundaries in parallel** \u2014 each rank only adds its\nown incident triangles and never sums the neighbouring rank's. So in parallel\nwe assemble it through the FE mass matrix instead (``_lumped_vertex_volumes``),\nwhere PETSc does the cross-rank ``localToGlobal(ADD)`` for us. Requires the\nP1 ``vol_field``; falls back to the local sum when it is not supplied.", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "__dealloc__", - "kind": "method", - "file": "src/underworld3/function/_dminterp_wrapper.pyx", - "line": 191, - "signature": "def __dealloc__(self):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_lumped_vertex_volumes", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 1071, + "signature": "(vol_field)", + "parameters": [ + { + "name": "vol_field", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": null, - "is_public": false - }, - { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/function/_dminterp_wrapper.pyx", - "line": 203, - "signature": "def __repr__(self):", - "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Parallel-correct per-vertex dual-patch volume = the lumped P1 mass\ndiagonal ``M_ii = \u222b N_i dV`` of ``vol_field``'s (P1, continuous, scalar)\nspace, assembled via the FE mass matrix so the cross-rank sum over shared\npartition-boundary vertices is done by PETSc \u2014 unlike the hand-rolled local\nsum in :func:`_patch_volumes`, which under-counts those vertices in parallel.\n\nIdentity: by partition of unity (``\u03a3_j N_j \u2261 1``) the consistent mass matrix\nhas row sums ``\u03a3_j M_ij = \u222b N_i \u03a3_j N_j = \u222b N_i dV``, i.e. the lumped diagonal\nis ``M\u00b71``.\n\nTODO(petsc4py): PETSc has a purpose-built\n``DMCreateMassMatrixLumped(dm, &llm, &lm)`` that returns this lumped diagonal\ndirectly (with the cross-rank ADD built in), but petsc4py (3.25) does not bind\nit yet \u2014 only the *consistent* ``DM.createMassMatrix`` is exposed, hence the\n``M\u00b71`` below. Replace this body with ``subdm.createMassMatrixLumped()`` once\npetsc4py exposes that DM method.\n\nReturns a per-vertex numpy array in ``vol_field``'s local DOF ordering (the\nsame depth-0 vertex ordering the movers use for ``vol_field.array``).", + "harvested_comments": [ + "consistent P1 mass (FE-assembled, parallel-correct)", + "M\u00b71 = row sums = lumped diagonal" ], - "parent_class": null, - "is_public": false - }, - { - "name": "_latex", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 66, - "signature": "def _latex(self, printer, exp=None):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], "parent_class": null, "is_public": false }, { - "name": "__new__", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 131, - "signature": "def __new__(cls,\n name : str,\n meshvar : underworld3.discretisation.MeshVariable,\n vtype : underworld3.VarType,\n component: Union[int, tuple] = 0,\n data_loc: int = None,\n *args, **options):", + "name": "_hessian_recovery_class", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 1114, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Lazily build (and memoise) the variationally-consistent\nHessian-recovery solver class.\n\nRecovers ``H_ij \u2248 \u2202\u00b2\u03c6/\u2202x_i\u2202x_j`` from an external scalar field\n``\u03c6`` by the *weak* (integrated-by-parts) form \u2014 the plan's\n:math:`R_H`: ``\u222bH_ij \u03c4_ij + \u222b(\u2202\u03c6/\u2202x_i)(\u2202\u03c4_ij/\u2202x_j) = 0`` \u21d2\n``H_ij = \u2202\u00b2\u03c6/\u2202x_i\u2202x_j``. Only **first** derivatives of ``\u03c6``\nappear (UW3 forbids second derivatives of mesh-variable\nfunctions); the operator is the SPD mass matrix (no nullspace).\nDefined lazily to avoid an import cycle (meshing\u2192systems/cython).", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_collect_mesh_varfns", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 182, - "signature": "def _collect_mesh_varfns(mesh):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_winslow_elliptic", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 1171, + "signature": "(mesh, metric, pinned_labels, verbose, n_outer = 1, n_picard = 25, relax = 1.0, step_frac = None, picard_relax = 0.4, outer_tol = 0.001, boundary_slip = False, linear_solver = 'direct', phi_degree = 2, move_anisotropy = None, target_side_rho = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "metric", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pinned_labels", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_outer", + "type_hint": null, + "default": "1", + "description": "" + }, + { + "name": "n_picard", + "type_hint": null, + "default": "25", + "description": "" + }, + { + "name": "relax", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "step_frac", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "picard_relax", + "type_hint": null, + "default": "0.4", + "description": "" + }, + { + "name": "outer_tol", + "type_hint": null, + "default": "0.001", + "description": "" + }, + { + "name": "boundary_slip", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "linear_solver", + "type_hint": null, + "default": "'direct'", + "description": "" + }, + { + "name": "phi_degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "move_anisotropy", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "target_side_rho", + "type_hint": null, + "default": "False", + "description": "" + } ], - "parent_class": null, - "is_public": false - }, - { - "name": "_lambdify_and_evaluate", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 203, - "signature": "def _lambdify_and_evaluate(expr, coords, interpolated_results, coord_sys=None, mesh=None):", - "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "Metric-driven mesh equidistribution \u2014 Benamou\u2013Froese\u2013Oberman\nconvex-branch Monge\u2013Amp\u00e8re (PRESERVED; not the default path).\n\nSolves ``det(I+D\u00b2\u03c6)=g``, ``g=c\u00b7\u03c1_cur/\u03c1_tgt``, by a damped Picard\non the convex-branch source\n``\u0394\u03c6 = \u221a((\u03c6xx\u2212\u03c6yy)\u00b2+4\u03c6xy\u00b2+4g) \u2212 2`` (the +\u221a selects the Brenier\nbranch), with the variationally-consistent recovered Hessian\n(``_hessian_recovery_class``) and the pure-Neumann\n``constant_nullspace`` \u03c6 Poisson. ``n_outer>1`` composes maps\n(recompute \u03c1_cur from patch volumes each step). Moves nodes by\n\u2207\u03c6 with a coherent global signed-area backtrack.\n\nEfficiency (2026-05-17): the \u03c6 Poisson and the SPD Hessian-recovery\nmass system are *constant operators* within the Picard loop (the\nmesh is fixed; only the RHS changes). ``_use_direct_solver`` puts\nboth on MUMPS LU with a lagged (compute-once) factorisation, so the\ninner iterations are back-substitutions \u2014 see that function's\ndocstring. ``n_picard`` defaults to 25: the deep/near grading is\nflat from iter \u224820 (4-dp identical at AMP 8 & 20), so 40 was pure\noverhead. Net: ~10\u00d7 faster, grading/quality bit-for-bit unchanged.\n\n``phi_degree`` defaults to **2** (was 3). The deep/near grading\nis set by the \u03c6 *order*, not the solver: P2 \u2261 P3 to ~3 dp across\nAMP 0/2/8/20 (matches the recorded baseline; AMP=0 no-op exact;\nno tangle) while P2 halves the cost (smaller matrices \u2014 also\nhelps the direct factorisation scale). P1 is **not**\ngrading-equivalent (\u22481.40 vs 1.71 at AMP=8 \u2014 ~18 % weaker); P2\nis the floor. ``linear_solver=\"gamg\"`` is an experimental,\ndocumented-fragile parallel prototype (P3 was a major GAMG\nconfound; even at P2 GAMG re-solve is erratic \u2014 see the design\ndoc); ``\"direct\"`` (MUMPS, MPI-parallel) is the validated path.\n\nGrading: redistribution with a fixed node count reaches deep/near\n\u22481.5\u20131.8\u00d7 for an 8\u201320\u00d7 density target (the exact OT ~10\u00d7 needs\n*more nodes* \u2014 a topology change, not this smoother). ``n_outer=1``\nis the safe default (AMP=0 exact no-op, never tangles). See the\nproject memory + scripts/ma_*.py / ma_cost_grading.py.", + "harvested_comments": [ + "\u2207\u03c6 / recovered-Hessian", + "Boundary tangential slip via the mesh-owned contract", + "(boundary-slip-strategy.md): MA's natural Neumann BC (\u2207\u03c6\u00b7n\u0302=0) makes", + "\u2207\u03c6 tangential at the boundary, so slip vertices slide along their", + "surface (radial ring / box face / facet) and snap back; non-slip," + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_project_to_work_variable", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 458, - "signature": "def _project_to_work_variable(expr, mesh, smoothing=1e-6):", - "parameters": [], + "name": "_winslow_equidistribute", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 1462, + "signature": "(mesh, metric, pinned_labels, verbose, n_outer = 1, relax = 1.0, step_frac = 0.3, outer_tol = 0.0001, boundary_slip = False, linear_solver = 'direct', phi_degree = 2)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "metric", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pinned_labels", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_outer", + "type_hint": null, + "default": "1", + "description": "" + }, + { + "name": "relax", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "step_frac", + "type_hint": null, + "default": "0.3", + "description": "" + }, + { + "name": "outer_tol", + "type_hint": null, + "default": "0.0001", + "description": "" + }, + { + "name": "boundary_slip", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "linear_solver", + "type_hint": null, + "default": "'direct'", + "description": "" + }, + { + "name": "phi_degree", + "type_hint": null, + "default": "2", + "description": "" + } + ], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "existing_docstring": "OT-improvement step: one (or a few) weighted-Poisson\nequidistribution flow iterations.\n\nSolves on the *current* mesh\n\n.. math::\n\n \\nabla\\!\\cdot(\\rho\\,\\nabla\\phi)\n \\;=\\;-\\,\\rho\\,\\log\\!\\bigl(V\\rho/K\\bigr),\n \\quad K=\\exp(\\langle\\rho\\log(V\\rho)\\rangle/\\langle\\rho\\rangle),\n \\quad \\nabla\\phi\\cdot\\hat{n}=0,\n\nand moves nodes by ``relax \u00b7 \u2207\u03c6``. ``V_i`` is the dual patch\narea at vertex ``i``; the source vanishes identically at\nequidistribution ``V_i\\,\\rho_i\\equiv K``.\n\nSemantics: this is a *single OT improvement step* w.r.t. the\ncurrent mesh \u2014 the input mesh has no special status (it is\nwhatever you currently have). Calling it again from the\ndeformed mesh applies another improvement step. Compose\nfreely with spring / smoothing / anisotropic.\n\nDifferences from ``_winslow_elliptic`` (the convex-branch\nBFO Picard):\n\n* Linear: one weighted-Poisson per outer iter, no inner\n Picard, no Hessian recovery, no convex-branch radical.\n* The source uses the *current* mesh's patch volumes; the\n formulation is identically zero at equidistribution, so\n iterations are self-stabilising (no over-correction).\n* \u03c1 at the current node positions (no source-vs-target\n asymmetry; the iteration is on the current mesh, \u03c1 is at\n its physical position).\n\nParameters mirror ``_winslow_elliptic`` where they apply.\n``n_outer`` composes outer improvement steps; the source\ndrives toward zero so the per-iter motion naturally\ndiminishes.", + "harvested_comments": [ + "Boundary slip uses the projected boundary-normal field", + "(mesh.Gamma_P1). This is reliable only for *radial* coordinate", + "systems (cylindrical / spherical / geographic), where mesh.Gamma is", + "the coordinate-derived radial field and evaluates cleanly at vertices.", + "For Cartesian boundaries the vertex-evaluated facet normal is" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_clement_to_work_variable", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 539, - "signature": "def _clement_to_work_variable(expr, mesh, derivfns):", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_winslow_anisotropic", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 1727, + "signature": "(mesh, metric, pinned_labels, verbose, n_outer = 12, relax = 0.2, beta = 200.0, resolution_ratio = 1.0, geom_mean_smoothing = 0.25, aniso_to_base = False, aniso_cap = 2.0, coarsen_cap = 1.0, boundary_slip = False, linear_solver = 'direct', phi_degree = 2, move_anisotropy = None, metric_role = 'M', outer_tol = 0.0001, rest_size_cap_max = None, rest_size_cap_min = None, rest_spring_K = 1.0, h0_override = None, rest_coords_override = None, metric_refresh_per_iter = False)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "metric", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "pinned_labels", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_outer", + "type_hint": null, + "default": "12", + "description": "" + }, + { + "name": "relax", + "type_hint": null, + "default": "0.2", + "description": "" + }, + { + "name": "beta", + "type_hint": null, + "default": "200.0", + "description": "" + }, + { + "name": "resolution_ratio", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "geom_mean_smoothing", + "type_hint": null, + "default": "0.25", + "description": "" + }, + { + "name": "aniso_to_base", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "aniso_cap", + "type_hint": null, + "default": "2.0", + "description": "" + }, + { + "name": "coarsen_cap", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "boundary_slip", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "linear_solver", + "type_hint": null, + "default": "'direct'", + "description": "" + }, + { + "name": "phi_degree", + "type_hint": null, + "default": "2", + "description": "" + }, + { + "name": "move_anisotropy", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "metric_role", + "type_hint": null, + "default": "'M'", + "description": "" + }, + { + "name": "outer_tol", + "type_hint": null, + "default": "0.0001", + "description": "" + }, + { + "name": "rest_size_cap_max", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "rest_size_cap_min", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "rest_spring_K", + "type_hint": null, + "default": "1.0", + "description": "" + }, + { + "name": "h0_override", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "rest_coords_override", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "metric_refresh_per_iter", + "type_hint": null, + "default": "False", + "description": "" + } ], - "parent_class": null, - "is_public": false - }, - { - "name": "_dmswarm_get_migrate_type", - "kind": "method", - "file": "src/underworld3/function/_function.pyx", - "line": 1265, - "signature": "def _dmswarm_get_migrate_type(sdm):", - "parameters": [], "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "existing_docstring": "Anisotropic metric-tensor mesh redistribution \u2014 approach (3).\n\nThe settled scalar equidistribution paths (``_winslow_spring``,\n``_winslow_elliptic``) cannot do coherent *anisotropic* bulk\ntransport on a fixed topology \u2014 a scalar potential is isotropic,\nso an annulus radial feature over-collapses one pinned-boundary\nsliver layer while the tangential edges sit frozen (see the\nproject memory + the design doc's angular-OT section). This is\nthe **tensor** mover: it solves the M-weighted Laplace smooth of\nthe coordinate map with an *anisotropic* metric tensor, so cells\nare reshaped (short across the feature, long along it) and the\nslivers / wasted isotropic resolution are removed.\n\nConstruction (verified \u2014 ``scripts/ma_metric_tensor_viz.py``):\nfrom a scalar density ``\u03c1`` (typically Lagrangian\n``f(r0.sym)``), the *projected* gradient ``\u2207\u03c1`` (a first\nderivative only \u2014 UW3-clean) builds, per node,\n\n.. math::\n\n M \\;=\\; \\tfrac1{h_0^2}\\!\\left[\\,I\n + \\beta\\,\\hat g\\hat g^{\\mathsf T}\n (|\\nabla\\rho|/\\nabla\\rho_{\\mathrm{ref}})^2\\right],\n\neigen-clamped so the spacing ratio ``\u2264 aniso_cap`` (``\u22648:1`` by\ndefault). The eigenframe **auto-aligns to the feature** from the\nCartesian ``\u2207\u03c1`` alone \u2014 no ``(r,\u03b8)`` frame is specified.\n\nMover: solve, per physical coordinate component ``c``, the\ndisplacement form of the M-weighted Laplace (Winslow) map\n\n.. math::\n\n \\nabla\\!\\cdot(D\\,\\nabla u_c) \\;=\\;\n -\\,\\nabla\\!\\cdot(D\\,e_c)\n \\;=\\; -\\textstyle\\sum_j \\partial_j D_{jc},\n \\qquad u_c = 0 \\text{ on the pinned boundary},\n\nwith ``D = M`` (the eigen-clamped metric). Then\n``\u03c8_c = x_c + u_c`` is exactly the M-harmonic coordinate map\n``\u2207\u00b7(D\u2207\u03c8_c)=0``, ``\u03c8=x`` on the boundary; the direct Winslow\nsmoother clusters nodes where ``D`` is large (fine spacing), so\n``D = M`` grades the mesh toward the metric. The two components\nshare the **same** tensor operator (``_c = D``, the\n``_CofDiff``-style ``DiffusionModel`` pattern) and the\nfactor-once-reuse direct solver. **Linear** \u2014 one solve per\ncomponent per outer step, no Picard (much cheaper than the BFO\n``_winslow_elliptic``). Homogeneous Dirichlet ``u=0`` on the\npinned boundary makes the per-component operator non-singular \u2014\nno ``constant_nullspace``, side-stepping the GAMG-pure-Neumann\nfragility entirely (``boundary_slip=True`` falls back to the\npure-Neumann + ring-projection treatment of\n``_winslow_elliptic``). ``n_outer`` composes the map (re-project\n``\u2207\u03c1`` / rebuild ``D`` on the moved mesh \u2014 the standard MMPDE\nouter iteration). Reuses ``_winslow_elliptic``'s coherent global\nsigned-area backtrack, ``boundary_slip`` and ``move_anisotropy``.\n\n.. warning::\n\n (3) improves cell **alignment / quality** and removes the\n slivers + wasted isotropic resolution; it does **not** beat\n the fixed node-count grading cap (\u22481.5\u20131.8\u00d7 for an 8\u201320\u00d7\n density target \u2014 that needs ``mesh.adapt``, a topology\n change). For a *separable* feature the explicit 1-D OT\n (``scripts/ma_analytic_check.py`` /\n ``ma_angular_ot_target.py``) is exact and strictly cheaper;\n (3) earns its keep on the general **non-separable** case.\n Validate with anisotropy-aware diagnostics\n (radial/tangential edge split + minA/meanA, *not* the\n anisotropy-blind d/n).\n\nParameters mirror ``_winslow_elliptic`` where shared.\n\nThe **decoupled direct** Winslow form (each physical coordinate\nM-harmonic, independently) has no Rado\u2013Kneser\u2013Choquet\nnon-folding guarantee, so its stable regime is bounded by the\nmetric anisotropy/contrast. Empirically (interior radial\nfeature, the validation arc) there is a clean Pareto frontier:\n\n* ``aniso_cap=2``, ``relax\u22480.1\u20130.2`` \u2192 minA/meanA \u2248 0.5 (a\n near-pristine, valid, feature-aligned mesh \u2014 cleaner than the\n isotropic MA \u22480.18 / spring \u22480.25 which sliver), modest 2:1\n cell alignment. **The robust default.**\n* higher ``aniso_cap`` is only stable with a *gentler* ``relax``\n + more ``n_outer`` (cap 4 needs relax \u22480.05, n_outer \u227325 \u2192\n minA \u22480.35, sharper alignment). ``aniso_cap \u2273 6`` folds the\n decoupled map regardless \u2014 it would need the coupled / inverse\n Winslow (the heavy MMPDE, out of this prototype's scope).\n\n**Single-knob model (`resolution_ratio` R).** The gradient-only\nmetric ``M \u2ab0 base\u00b7I`` is *refine-only* (keeps only ``\u2207\u03c1``,\ndiscards \u03c1's magnitude \u21d2 flat cells pinned at ``h0``, cannot\nrelease nodes, the steepest feature scavenges the budget). The\nfix makes the isotropic density a genuinely **equidistributed**\nfield ``s = base\u00b7\u03c1/G`` (``G`` = geometric mean of \u03c1 on the\nnear-uniform undeformed D mesh \u21d2 ``\u27e8ln s\u27e9=ln base``, node budget\ncentred). Refine (``s>base``) and coarsen (``s=2` must match serial): the\nper-element `d\u00d7d` algebra is rank-local (batched ``numpy.linalg``);\nthe **velocity assembly** `\u03a3_{K\u220bi}|K|v^K_i` is summed over **owned\ncells** into the coordinate DM Vec with ``localToGlobal(ADD_VALUES)``\n+ ``globalToLocal`` (cross-rank ghost reduction \u2014 not ``np.add.at``\ninto a global array); the per-node step and the energy/area\nline-search predicates are computed from owned/assembled data with\ncollective ``allreduce`` so every rank takes the same accept/backtrack\nbranch; only owned vertices move and ghosts are halo-synced each trial\nso the final ``_deform_mesh`` is consistent.\n\nTime integration: gradient flow `dx_i/dt = (P_i/\u03c4)\u03a3|K|v`,\n`P_i = detM(x_i)^{(p-1)/2}` (scale-free), explicit Euler with a\nper-node step cap (``step_frac``\u00b7min-incident-edge) and an **energy\nline-search backtrack** (accept only if no fold *and* `I_h`\ndecreases) so the descent is monotone. ``n_outer`` Euler steps.\n\nThe steepest-descent direction is accelerated by ``accel`` (default\n``\"cg\"``, nonlinear conjugate gradient, parameter-free): this cuts the\nouter-iteration count ~13\u00d7 on the first (uniform\u2192radial) adapt vs plain\ndescent and makes adapt-every-step affordable. ``\"heavyball\"`` /\n``\"hb-restart\"`` use Polyak momentum with coefficient ``momentum`` (default\n0.9 for those modes); ``\"none\"`` is plain descent. The line-search keeps\nevery accelerator fold-safe. (Previously controlled by the ``MMPDE_ACCEL`` /\n``MMPDE_MOMENTUM`` environment variables, now removed \u2014 pass as kwargs, e.g.\n``method_kwargs={\"accel\": \"cg\"}`` through ``smooth_mesh_interior``.)", + "harvested_comments": [ + "Guard here, before any metric parsing or DM work, so a 3D caller", + "gets an honest message rather than a NameError from the (never", + "implemented) 3D discretization deeper in the mover (READ-01).", + "Accept a full d\u00d7d SPD tensor (sympy Matrix or tensor MeshVariable) OR a", + "scalar density rho \u2014 the latter is coerced to the isotropic tensor rho*I," + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "__new__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 583, - "signature": "(cls, name, *args, **kwargs)", + "name": "_smooth_mesh_interior_bare", + "kind": "function", + "file": "src/underworld3/meshing/smoothing.py", + "line": 3541, + "signature": "(mesh, pinned_labels: Optional[Sequence[str]] = None, n_iters: int = 5, alpha: float = 0.5, metric = None, method: str = 'spring', boundary_slip: bool = False, method_kwargs: Optional[dict] = None, verbose: bool = False, skip_threshold = _UNSET, strategy: Optional[str] = None)", "parameters": [ { - "name": "name", + "name": "mesh", "type_hint": null, "default": null, "description": "" }, { - "name": "*args", + "name": "pinned_labels", + "type_hint": "Optional[Sequence[str]]", + "default": "None", + "description": "" + }, + { + "name": "n_iters", + "type_hint": "int", + "default": "5", + "description": "" + }, + { + "name": "alpha", + "type_hint": "float", + "default": "0.5", + "description": "" + }, + { + "name": "metric", "type_hint": null, - "default": null, + "default": "None", "description": "" }, { - "name": "**kwargs", + "name": "method", + "type_hint": "str", + "default": "'spring'", + "description": "" + }, + { + "name": "boundary_slip", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "method_kwargs", + "type_hint": "Optional[dict]", + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": "bool", + "default": "False", + "description": "" + }, + { + "name": "skip_threshold", "type_hint": null, - "default": null, + "default": "_UNSET", + "description": "" + }, + { + "name": "strategy", + "type_hint": "Optional[str]", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Internal mover dispatch \u2014 no transfer, no helper wrap.\n\nIdentical to the body of :func:`smooth_mesh_interior` minus the\nPhase-1 transfer wrap. Composite adapt ops (``_ot_adapt_step``,\n``follow_metric``) own the wrap at their level and call this bare\nform to avoid nesting the snapshot/restore dance. End-users should\nkeep using :func:`smooth_mesh_interior`.", "harvested_comments": [ - "If the expression already exists, return it", - "Check both dicts for name collisions", - "Check ephemeral dict - need to look for any key starting with this name", - "Determine unique ID for disambiguation", - "When _unique_name_generation=True, ALWAYS use instance_no as _uw_id" + "Resolve strategy defaults \u2014 individual kwargs override.", + "\"off\" \u2192 early-exit, mesh stays uniform.", + "method_kwargs: fill in resolution_ratio from strategy", + "if caller didn't already set it.", + "Skip-if-good-enough: compare current cell sizes to what" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__lt__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 681, - "signature": "(self, other)", + "name": "_to_nd_length", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 58, + "signature": "(value) -> float", "parameters": [ { - "name": "other", + "name": "value", "type_hint": null, "default": null, "description": "" } ], + "returns": "float", + "existing_docstring": "Convert a length value to nondimensional mesh coordinates.\n\nAccepts either a plain float (returned as-is) or a unit-aware quantity\n(e.g., ``uw.quantity(10, \"km\")``) which is nondimensionalised via the\nmodel's reference scales.\n\nThis helper is used by Surface methods that accept physical distances\n(``h_near``, ``h_far``, ``width``, etc.) so that users can specify\ndistances in natural units rather than manually dividing by a reference\nlength.\n\nParameters\n----------\nvalue : float or UWQuantity\n A length value. If it has a ``.magnitude`` attribute (Pint/UWQuantity),\n it is nondimensionalised. Plain numbers pass through unchanged.\n\nReturns\n-------\nfloat\n Nondimensional value in mesh coordinate space.", + "harvested_comments": [ + "UWQuantity or Pint quantity \u2014 nondimensionalise via the units system", + "non_dimensionalise may return UWQuantity or scalar \u2014 extract float", + "Fallback: try to extract raw magnitude" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_require_pyvista", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 100, + "signature": "()", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Check pyvista availability with helpful error message.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": null, "is_public": false }, { - "name": "__le__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 688, - "signature": "(self, other)", + "name": "_depth_to_km", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 113, + "signature": "(value) -> float", "parameters": [ { - "name": "other", + "name": "value", "type_hint": null, "default": null, "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "returns": "float", + "existing_docstring": "Convert a depth value to kilometres.\n\nAccepts a plain float (assumed km) or a ``uw.quantity`` which is\nconverted to km via Pint.", + "harvested_comments": [ + "UWQuantity / Pint quantity \u2014 convert to km" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWQuantity", + "parent_class": null, "is_public": false }, { - "name": "__gt__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 695, - "signature": "(self, other)", + "name": "_order_polyline", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 132, + "signature": "(points: np.ndarray) -> np.ndarray", "parameters": [ { - "name": "other", - "type_hint": null, + "name": "points", + "type_hint": "np.ndarray", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "returns": "np.ndarray", + "existing_docstring": "Order 2D points along a polyline.\n\nFirst removes near-duplicate points (within 1e-10 distance), then\nfinds the two true endpoints of the trace (the farthest-apart pair)\nand chains from one endpoint using nearest-neighbour traversal.\n\nStarting from a true endpoint avoids the common failure mode where\nnearest-neighbour begins mid-trace and creates loops at bends.\n\nParameters\n----------\npoints : ndarray, shape (N, 2)\n Unordered 2D points.\n\nReturns\n-------\nndarray, shape (M, 2)\n Points reordered along the polyline (M <= N after dedup).", + "harvested_comments": [ + "Remove near-duplicate points", + "Find the farthest-apart pair \u2014 these are the trace endpoints.", + "For small N (typical fault traces), brute-force pairwise is fine.", + "begin from one endpoint", + "Nearest-neighbour chain from the endpoint" ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__ge__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 702, - "signature": "(self, other)", + "name": "_interpolate_trace", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 186, + "signature": "(points: np.ndarray, target_spacing: float, smoothing: float = 0.0, is_geographic: bool = False) -> np.ndarray", "parameters": [ { - "name": "other", - "type_hint": null, + "name": "points", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "target_spacing", + "type_hint": "float", "default": null, "description": "" + }, + { + "name": "smoothing", + "type_hint": "float", + "default": "0.0", + "description": "" + }, + { + "name": "is_geographic", + "type_hint": "bool", + "default": "False", + "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "returns": "np.ndarray", + "existing_docstring": "Interpolate a 2D polyline to a target point spacing.\n\nUses scipy parametric spline fitting (``splprep`` / ``splev``).\n\nParameters\n----------\npoints : ndarray, shape (N, 2)\n Ordered polyline vertices (lon/lat or x/y).\ntarget_spacing : float\n Desired spacing between output points. For geographic traces\n this is in km; for Cartesian traces it is in model coordinates.\nsmoothing : float\n Spline smoothing parameter (0 = exact interpolation).\nis_geographic : bool\n If True, arc lengths are estimated in km using a rough\n degree-to-km conversion (~111 km/deg).\n\nReturns\n-------\nndarray, shape (M, 2)\n Resampled polyline with approximately *target_spacing* between\n consecutive points.", + "harvested_comments": [ + "Estimate cumulative arc length", + "Rough conversion: 1 degree \u2248 111 km (adequate for spacing estimates)", + "Fit parametric spline", + "Fallback: linear interpolation along arc length" ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__eq__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 709, - "signature": "(self, other)", + "name": "_profile_to_edge_lengths", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 250, + "signature": "(dist_values: np.ndarray, h_near: float, h_far: float, width: float, profile: str) -> np.ndarray", "parameters": [ { - "name": "other", - "type_hint": null, + "name": "dist_values", + "type_hint": "np.ndarray", + "default": null, + "description": "" + }, + { + "name": "h_near", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "h_far", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "width", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "profile", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": null, + "returns": "np.ndarray", + "existing_docstring": "Compute target edge lengths from distance values using a profile function.\n\nParameters\n----------\ndist_values : ndarray\n Unsigned distance values at each mesh node.\nh_near : float\n Target edge length near the surface.\nh_far : float\n Target edge length far from the surface.\nwidth : float\n Transition distance from h_near to h_far.\nprofile : str\n One of \"linear\", \"smoothstep\", or \"gaussian\".\n\nReturns\n-------\nndarray\n Target edge lengths, same shape as *dist_values*.", "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__ne__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 716, - "signature": "(self, other)", + "name": "_compute_trace_perpendicular", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 294, + "signature": "(trace_points: np.ndarray, direction: str = 'right', is_geographic: bool = False) -> np.ndarray", "parameters": [ { - "name": "other", - "type_hint": null, + "name": "trace_points", + "type_hint": "np.ndarray", "default": null, "description": "" + }, + { + "name": "direction", + "type_hint": "str", + "default": "'right'", + "description": "" + }, + { + "name": "is_geographic", + "type_hint": "bool", + "default": "False", + "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "returns": "np.ndarray", + "existing_docstring": "Compute the perpendicular direction at each point of a 2D trace.\n\nReturns vectors such that ``offset_km * result`` (geographic) or\n``offset * result`` (Cartesian) gives the correct horizontal\ndisplacement perpendicular to the trace.\n\nParameters\n----------\ntrace_points : ndarray, shape (N, 2)\n Ordered trace points \u2014 ``(lon, lat)`` for geographic or\n ``(x, y)`` for Cartesian.\ndirection : str\n ``\"right\"`` or ``\"left\"`` relative to the trace direction\n (from first point to last).\nis_geographic : bool\n If True, account for the latitude-dependent metric when\n computing directions and return results in degrees so that\n ``offset_km * perp`` gives the geographic offset.\n\nReturns\n-------\nndarray, shape (N, 2)\n Perpendicular direction at each trace point.", + "harvested_comments": [ + "Central differences for interior, forward/backward for endpoints", + "Normalise to unit length", + "Rotate 90\u00b0 to get perpendicular", + "Convert back to degrees (so offset_km * perp gives degree offset)" ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__repr__", + "name": "_create_proxy", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 339, - "signature": "(self)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 550, + "signature": "(self) -> None", "parameters": [], - "returns": null, - "existing_docstring": null, + "returns": "None", + "existing_docstring": "Create the proxy MeshVariable for .sym access.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "MaterialRegistry", + "parent_class": "SurfaceVariable", "is_public": false }, { - "name": "__repr__", + "name": "_interpolate_to_proxy", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 366, - "signature": "(self) -> str", + "file": "src/underworld3/meshing/surfaces.py", + "line": 559, + "signature": "(self) -> None", "parameters": [], - "returns": "str", - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "returns": "None", + "existing_docstring": "Interpolate surface vertex data to mesh nodes.\n\nEach rank populates its LOCAL mesh nodes only using inverse distance\nweighting from surface vertices.", + "harvested_comments": [ + "Get local mesh node coordinates in model (internal) space", + "to match surface vertex coordinates", + "Get surface vertex data in model (internal) space \u2014 bypass the", + "dimensionalising output gateway so coordinates match mesh._coords.", + "For 2D surfaces, use only x,y components for KDTree" ], - "parent_class": "FaultSurface", - "is_public": false - }, - { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 644, - "signature": "(self) -> str", - "parameters": [], - "returns": "str", - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "FaultCollection", + "parent_class": "SurfaceVariable", "is_public": false }, { - "name": "__repr__", + "name": "_dimensionalise_coords", "kind": "method", "file": "src/underworld3/meshing/surfaces.py", - "line": 301, - "signature": "(self) -> str", - "parameters": [], - "returns": "str", - "existing_docstring": null, - "harvested_comments": [ - "Get raw data length to avoid triggering UnitAwareArray" + "line": 769, + "signature": "(self, coords: np.ndarray) -> np.ndarray", + "parameters": [ + { + "name": "coords", + "type_hint": "np.ndarray", + "default": null, + "description": "" + } ], - "status": "none", + "returns": "np.ndarray", + "existing_docstring": "Apply dimensional scaling to internal model coordinates.\n\nFollows the same gateway pattern as ``mesh.X.coords``: internal\nstorage is in model (ND) space; user-facing properties return\nphysical (dimensional) coordinates when the units system is active.\n\nReturns the original array unchanged when no units are configured.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceVariable", + "parent_class": "Surface", "is_public": false }, { - "name": "__repr__", + "name": "_mark_all_proxies_stale", "kind": "method", "file": "src/underworld3/meshing/surfaces.py", - "line": 1237, - "signature": "(self) -> str", + "line": 855, + "signature": "(self) -> None", "parameters": [], - "returns": "str", - "existing_docstring": null, + "returns": "None", + "existing_docstring": "Mark all SurfaceVariable proxies as stale.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], "parent_class": "Surface", "is_public": false }, { - "name": "__repr__", + "name": "_ensure_discretized", "kind": "method", "file": "src/underworld3/meshing/surfaces.py", - "line": 1550, - "signature": "(self) -> str", + "line": 945, + "signature": "(self) -> None", "parameters": [], - "returns": "str", - "existing_docstring": null, + "returns": "None", + "existing_docstring": "Ensure discretization is computed (lazy evaluation).", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", + "parent_class": "Surface", "is_public": false }, { - "name": "__enter__", + "name": "_discretize_3d", "kind": "method", - "file": "src/underworld3/mpi.py", - "line": 396, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "file": "src/underworld3/meshing/surfaces.py", + "line": 977, + "signature": "(self, offset: float = 0.01) -> None", + "parameters": [ + { + "name": "offset", + "type_hint": "float", + "default": "0.01", + "description": "" + } + ], + "returns": "None", + "existing_docstring": "Discretize 3D control points into triangulated mesh using pyvista delaunay_2d.", + "harvested_comments": [ + "Check for degenerate cases (all points nearly collinear)", + "Create PolyData from points and triangulate", + "Compute normals (both point and cell)" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "call_pattern", + "parent_class": "Surface", "is_public": false }, { - "name": "__exit__", + "name": "_discretize_2d", "kind": "method", - "file": "src/underworld3/mpi.py", - "line": 402, - "signature": "(self, *args)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1009, + "signature": "(self, n_segments: int = None) -> None", "parameters": [ { - "name": "*args", - "type_hint": null, - "default": null, + "name": "n_segments", + "type_hint": "int", + "default": "None", "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "returns": "None", + "existing_docstring": "Create 2D surface as ordered line segments using scipy spline fitting.\n\nFor 2D, a \"surface\" is a 1D curve (polyline) embedded in 2D space.", + "harvested_comments": [ + "Get 2D coordinates", + "For just 2 points, connect them directly", + "Use scipy to fit a parametric spline through points", + "This naturally orders points along the curve", + "s=0 means interpolate exactly through points" + ], + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "call_pattern", + "parent_class": "Surface", "is_public": false }, { - "name": "__repr__", + "name": "_compute_distance_field", "kind": "method", - "file": "src/underworld3/parameters.py", - "line": 317, - "signature": "(self)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1172, + "signature": "(self) -> None", "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", + "returns": "None", + "existing_docstring": "Compute signed distance field from mesh nodes to surface.\n\nThe signed distance is positive on one side of the surface and\nnegative on the other. Helper functions like influence_function()\nuse sympy.Abs() when unsigned distance is needed.\n\nFor 3D surfaces: Uses pyvista's compute_implicit_distance.\nFor 2D surfaces: Uses geometry_tools signed_distance_pointcloud_polyline_2d.", + "harvested_comments": [ + "Use varsymbol for clean LaTeX display: d_{F} instead of {surf_fault_distance}", + "Always wrap symbol in braces for proper LaTeX grouping (e.g., d_{F_1} not d_F_1)", + "Get mesh coordinates in model (internal) space.", + "Must use raw model coordinates (mesh._coords) rather than mesh.X.coords", + "because mesh.X.coords returns dimensional/scaled coordinates when units" + ], + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "ParameterRegistry", + "parent_class": "Surface", "is_public": false }, { - "name": "_data_layout", + "name": "_on_mesh_adapted", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 958, - "signature": "(self, i, j = None)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 1430, + "signature": "(self, adapted_mesh: 'Mesh') -> None", "parameters": [ { - "name": "i", - "type_hint": null, + "name": "adapted_mesh", + "type_hint": "'Mesh'", "default": null, "description": "" - }, - { - "name": "j", - "type_hint": null, - "default": "None", - "description": "" } ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "SwarmVariable", - "is_public": false - }, - { - "name": "_create_proxy_variable", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1005, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, + "returns": "None", + "existing_docstring": "Called by mesh.adapt() to update after mesh adaptation.\n\nMarks the distance field as stale so it will be recomputed on next access.\nThe surface geometry (control points, pyvista mesh) is unchanged -\nonly the cached distance values need updating.\n\nThe distance MeshVariable itself is reinitialized by mesh.adapt() along\nwith all other MeshVariables - we just need to mark the data as stale.\n\nArgs:\n adapted_mesh: The mesh (same object, updated internals)", "harvested_comments": [ - "release if defined" + "Mark distance as stale - will be recomputed on next access", + "The MeshVariable stays in mesh._vars and gets reinitialized by adapt()", + "just like any other variable (same pattern as swarm proxy variables)", + "Mark all variable proxies as stale (they project to mesh nodes)" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SwarmVariable", + "parent_class": "Surface", "is_public": false }, { "name": "__getitem__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2048, - "signature": "(self, index)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2034, + "signature": "(self, name: str) -> Surface", "parameters": [ { - "name": "index", - "type_hint": null, + "name": "name", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": null, + "returns": "Surface", + "existing_docstring": "Get a surface by name.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "SurfaceCollection", "is_public": false }, { - "name": "_particle_coordinates", - "kind": "property", - "file": "src/underworld3/swarm.py", - "line": 2705, + "name": "__iter__", + "kind": "method", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2038, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Iterate over surface names.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "SurfaceCollection", "is_public": false }, { - "name": "_data_layout", + "name": "__len__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3952, - "signature": "(self, i, j = None)", - "parameters": [ - { - "name": "i", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "j", - "type_hint": null, - "default": "None", - "description": "" - } - ], + "file": "src/underworld3/meshing/surfaces.py", + "line": 2042, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Number of surfaces in collection.", "harvested_comments": [], - "status": "none", + "status": "minimal", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "SurfaceCollection", "is_public": false }, { - "name": "_get_map", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3981, - "signature": "(self, var)", + "name": "_fault_seg_distance_sym", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2423, + "signature": "(X, a, b)", "parameters": [ { - "name": "var", + "name": "X", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "generate tree if not avaiable", - "get or generate map", - "we can't use numpy arrays directly as keys in python dicts, so", - "we'll use `xxhash` to generate a hash of array.", - "this shouldn't be an issue performance wise but we should test to be" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "Swarm", - "is_public": false - }, - { - "name": "_data_layout", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1018, - "signature": "(self, i, j = None)", - "parameters": [ + }, { - "name": "i", + "name": "a", "type_hint": null, "default": null, "description": "" }, { - "name": "j", + "name": "b", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, + "existing_docstring": "Analytic point-to-segment distance (sympy) for the 2D segment a\u2192b.\n\nA pure function of the symbolic coordinates ``X`` and the FIXED segment\nendpoints, so it re-evaluates exactly (Eulerian) wherever sampled \u2014 the\nproperty the fault metric needs (a nodal distance field would bridge the\nsub-cell dip and convect under iteration).", "harvested_comments": [], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "PICSwarm", + "parent_class": null, "is_public": false }, { - "name": "_get_map", - "kind": "method", - "file": "src/underworld3/swarms/pic_swarm.py", - "line": 1046, - "signature": "(self, var)", + "name": "_fault_collect_polylines", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2440, + "signature": "(faults)", "parameters": [ { - "name": "var", + "name": "faults", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "generate tree if not avaiable", - "get or generate map", - "we can't use numpy arrays directly as keys in python dicts, so", - "we'll use `xxhash` to generate a hash of array.", - "this shouldn't be an issue performance wise but we should test to be" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "PICSwarm", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 121, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, + "existing_docstring": "Group ``faults`` into a list of per-fault segment lists.\n\nReturns ``[[(a,b), ...], ...]`` \u2014 one inner list per fault, holding the\nconsecutive segments of that fault's polyline. Preserving the per-fault\ngrouping matters for the comb metric, whose teeth are placed at distances\nfrom each fault's MIN-distance (so a curved/polyline fault gets bands that\nfollow the curve, not a tangle of per-segment bands).\n\n``faults`` may be a single :class:`Surface`, a single segment / polyline\narray, or a list mixing those. A :class:`Surface` contributes the segments\nof its control-point polyline (model-space, matching ``mesh.X``); an\n``(N, 2)``/``(N, 3)`` array a polyline (``N\u22652``).", "harvested_comments": [ - "Display the primary variable", - "Display the history variable using the different symbol." + "model space \u2261 mesh.X coords" ], - "status": "none", + "status": "partial", "needs": [ - "NEEDS_OVERVIEW", "NEEDS_PARAMETERS" ], - "parent_class": "Symbolic", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 335, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance", - "display(Latex(r\"$\\quad\\psi = $ \" + self.psi._repr_latex_()))", - "r\"$\\quad\\Delta t_{\\textrm{phys}} = $ \"", - "+ sympy.sympify(self.dt_physical)._repr_latex_()" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" + "name": "_fault_collect_segments", + "kind": "function", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2477, + "signature": "(faults)", + "parameters": [ + { + "name": "faults", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "Eulerian", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 701, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": null, + "existing_docstring": "Flatten ``faults`` into a single list of ``(a, b)`` 2D endpoint pairs\n(segment grouping discarded \u2014 used by the anisotropic tensor builder,\nwhere each segment contributes its own normal-aligned bump).", "harvested_comments": [], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "SemiLagrangian", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1214, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "Lagrangian", - "is_public": false - }, - { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 1418, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": null, - "harvested_comments": [ - "# feedback on this instance" - ], - "status": "none", - "needs": [ - "NEEDS_OVERVIEW", - "NEEDS_PARAMETERS" - ], - "parent_class": "Lagrangian_Swarm", - "is_public": false - }, - { - "name": "_units_view", - "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 201, - "signature": "()", - "parameters": [], - "returns": null, - "existing_docstring": "Display units registry information following the established view() pattern.", - "harvested_comments": [ - "# Units Registry", - "## Common Units Examples:", - "Create quantities", - "Set model reference quantities", - "Fallback for non-Jupyter environments" - ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_apply_scaling", + "name": "_mesh_h0", "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 471, - "signature": "()", - "parameters": [], + "file": "src/underworld3/meshing/surfaces.py", + "line": 2779, + "signature": "(mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Internal context manager - DEPRECATED, use use_nondimensional_scaling() instead.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Mean undeformed edge length (parallel-safe) \u2014 the mesh's\ncharacteristic cell size, used to translate an absolute ``cell_size``\ninto the anisotropic mover's relative refinement ratio.", + "harvested_comments": [ + "TRUE global mean edge length (sum/count), not a mean of per-rank means." + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_is_scaling_active", + "name": "_fault_min_distance_np", "kind": "function", - "file": "src/underworld3/__init__.py", - "line": 482, - "signature": "()", - "parameters": [], + "file": "src/underworld3/meshing/surfaces.py", + "line": 2798, + "signature": "(P, polylines)", + "parameters": [ + { + "name": "P", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "polylines", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Internal check - DEPRECATED, use is_nondimensional_scaling_active() instead.", + "existing_docstring": "Numpy min point-to-polyline distance from points ``P`` (k, 2) to all\nsegments of all faults \u2014 used to build the nodal MMG metric.", "harvested_comments": [], "status": "minimal", "needs": [ @@ -34581,2373 +59846,2777 @@ "is_public": false }, { - "name": "_dm_unstack_bcs", + "name": "_fault_mmg_metric", "kind": "function", - "file": "src/underworld3/adaptivity.py", - "line": 472, - "signature": "(dm, boundaries, stacked_bc_label_name)", + "file": "src/underworld3/meshing/surfaces.py", + "line": 2815, + "signature": "(mesh, faults, cell_size, n_across, h_far, name)", "parameters": [ { - "name": "dm", + "name": "mesh", "type_hint": null, "default": null, "description": "" }, { - "name": "boundaries", + "name": "faults", "type_hint": null, "default": null, "description": "" }, { - "name": "stacked_bc_label_name", + "name": "cell_size", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "n_across", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "h_far", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Unpack boundary labels to the list of names", + "existing_docstring": "Build the ``h\u207b\u00b2`` isotropic metric MeshVariable for ``mesh.adapt``\n(MMG): edge length ``cell_size`` in the band ``|d| < (n_across/2)\u00b7dx``,\nramping (smoothstep) to ``h_far`` outside. MMG *adds* nodes to honour\nthis absolute spacing, so the band is genuinely uniform.", "harvested_comments": [ - "Clear labels just in case", - "ValueError if mismatch" + "transition width", + "0 in band -> 1 far", + "isotropic h^-2 metric" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_q", + "name": "_register_mesh", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 418, - "signature": "(self, ddu)", + "file": "src/underworld3/model.py", + "line": 187, + "signature": "(self, mesh)", "parameters": [ { - "name": "ddu", + "name": "mesh", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Generic flux term", + "existing_docstring": "Internal method to register a mesh with this model.\nCalled automatically from Mesh.__init__\n\nParameters:\n-----------\nmesh : uw.discretisation.Mesh\n Mesh instance to register", "harvested_comments": [ - "tensor multiplication" + "Set as primary mesh if no primary mesh exists" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Constitutive_Model", + "parent_class": "Model", "is_public": false }, { - "name": "_reset", + "name": "_register_swarm", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 461, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Flags that the expressions in the consitutive tensor need to be refreshed and also that the\nsolver will need to rebuild the stiffness matrix and jacobians", - "harvested_comments": [ - "Propagate is_setup flag to solver if we have a reference" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 232, + "signature": "(self, swarm)", + "parameters": [ + { + "name": "swarm", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "Constitutive_Model", - "is_public": false - }, - { - "name": "_build_c_tensor", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 475, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Return the identity tensor of appropriate rank (e.g. for projections)", + "existing_docstring": "Internal method to register a swarm with this model.\nCalled automatically from Swarm.__init__\n\nParameters:\n-----------\nswarm : uw.swarm.Swarm\n Swarm instance to register", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "Constitutive_Model", + "parent_class": "Model", "is_public": false }, { - "name": "_q", + "name": "_unregister_swarm", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 622, - "signature": "(self, edot)", + "file": "src/underworld3/model.py", + "line": 245, + "signature": "(self, swarm)", "parameters": [ { - "name": "edot", + "name": "swarm", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Apply constitutive tensor to strain rate to compute stress.", + "existing_docstring": "Internal method to drop a swarm and all its variables from the registry.\n\nWithout this, transient swarms (used for global expression evaluation,\ncheckpoint reads, mesh adaptation transfers) accumulate in the registry\nforever, leaking the underlying PETSc DMs and field storage. Called\nfrom :meth:`Swarm.__del__`.", "harvested_comments": [ - "tensor multiplication" + "Drop any variables that belong to this swarm. Building the list of", + "names first avoids mutating the dict during iteration." ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "ViscousFlowModel", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_register_variable", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 661, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "For this constitutive law, we expect just a viscosity function", - "harvested_comments": [ - "Check for tensor forms first (Mandel matrix or full rank-4 tensor)", - "Mandel form of constitutive tensor", - "Full rank-4 tensor", - "Scalar viscosity case", - "UWexpression has __getitem__ from MathematicalMixin, making it Iterable," - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 270, + "signature": "(self, name, variable)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "variable", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "ViscousFlowModel", - "is_public": false - }, - { - "name": "_build_c_tensor", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1336, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "For this constitutive law, we expect just a viscosity function", + "existing_docstring": "Internal method to register a variable with this model.\nCalled automatically from MeshVariable/SwarmVariable.__init__\n\nHandles namespace collision by creating fully qualified names for variables\non different meshes/swarms but with the same symbolic name.\n\nParameters:\n-----------\nname : str\n Variable name (may not be unique across meshes/swarms)\nvariable : MeshVariable or SwarmVariable\n Variable instance to register", "harvested_comments": [ - "inner_self = self.Parameters", - "CRITICAL: Use .sym property to avoid UWexpression array corruption issues", - "See ViscousFlowModel._build_c_tensor() for detailed explanation" + "For SwarmVariables, make sure the parent swarm has been registered.", + "Registration is via WeakValueDictionary, so it does not pin the", + "swarm \u2014 when the user drops their last reference the swarm dies", + "and ``Swarm.__del__`` cleans up the variable side of the registry.", + "This will raise if swarm already garbage collected" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_register_solver", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1619, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Build isotropic diffusivity tensor from scalar.", - "harvested_comments": [ - "Scalar diffusivity case", - "Use element-wise construction (consistent with ViscousFlowModel pattern)", - "to handle UWexpression properly and preserve for JIT unwrapping", - "Diagonal element: kappa", - "Wrap if bare UWexpression to avoid Iterable check failure" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 370, + "signature": "(self, name, solver)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "DiffusionModel", - "is_public": false - }, - { - "name": "_build_c_tensor", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1711, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Constructs the anisotropic (diagonal) tensor from the diffusivity vector.", + "existing_docstring": "Internal method to register a solver with this model.\nCalled automatically from solver.__init__\n\nParameters:\n-----------\nname : str\n Solver name/identifier\nsolver : Solver instance\n Solver to register", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "AnisotropicDiffusionModel", - "is_public": false - }, - { - "name": "_build_c_tensor", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 1908, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "For this constitutive law, we expect just a permeability function", - "harvested_comments": [ - "Scalar permeability case", - "Use element-wise construction (consistent with ViscousFlowModel and DiffusionModel)", - "to handle UWexpression properly and preserve for JIT unwrapping", - "Diagonal element: kappa", - "Wrap if bare UWexpression to avoid Iterable check failure" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "DarcyFlowModel", - "is_public": false - }, - { - "name": "_build_c_tensor", - "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2077, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "For this constitutive law, we expect two viscosity functions\nand a sympy row-matrix that describes the director components n_{i}", - "harvested_comments": [ - "Use .sym to get sympy expressions from Parameters", - "Use element-wise construction (same pattern as ViscousFlowModel).", - "UWexpression has __getitem__ from MathematicalMixin, making it appear", - "\"Iterable\" to SymPy's array multiplication operator, which rejects it.", - "Element-wise construction avoids this by creating Mul objects that" - ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "TransverseIsotropicFlowModel", + "parent_class": "Model", "is_public": false }, { - "name": "_setup_shared_unknowns", + "name": "_update_mesh_for_swarm", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2218, - "signature": "(self, constitutive_models, unknowns)", + "file": "src/underworld3/model.py", + "line": 409, + "signature": "(self, swarm, new_mesh)", "parameters": [ { - "name": "constitutive_models", + "name": "swarm", "type_hint": null, "default": null, "description": "" }, { - "name": "unknowns", + "name": "new_mesh", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Ensure all constituent models share the solver's authoritative unknowns.\nThis is critical for proper stress history management.", + "existing_docstring": "Internal method to coordinate mesh handover for a specific swarm.\n\nThis method handles the model-level coordination when a swarm\nis assigned to a new mesh via swarm.mesh = new_mesh.\n\nParameters\n----------\nswarm : uw.swarm.Swarm\n Swarm being reassigned to new mesh\nnew_mesh : uw.discretisation.Mesh\n New mesh to assign\n\nNotes\n-----\nThis is called from swarm.mesh setter and handles:\n- Unregistering swarm from old mesh\n- Updating model's mesh reference\n- Registering swarm with new mesh", "harvested_comments": [ - "Share solver's unknowns - this gives access to composite D(F)/Dt history", - "Validation: Ensure sharing worked correctly", - "For elastic models, verify DFDt access" + "Unregister swarm from old mesh", + "Mesh may not support swarm registration or swarm not registered", + "Update model's mesh reference", + "Register swarm with new mesh" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "MultiMaterialConstitutiveModel", + "parent_class": "Model", "is_public": false }, { - "name": "_validate_model_compatibility", + "name": "_register_state_bearer", "kind": "method", - "file": "src/underworld3/constitutive_models.py", - "line": 2236, - "signature": "(self, models: list) -> bool", + "file": "src/underworld3/model.py", + "line": 602, + "signature": "(self, obj) -> None", "parameters": [ { - "name": "models", - "type_hint": "list", + "name": "obj", + "type_hint": null, "default": null, "description": "" } ], - "returns": "bool", - "existing_docstring": "Ensure all constituent models are compatible for flux averaging.\n\nChecks:\n- Same u_dim (scalar vs vector problem compatibility)\n- Same spatial dimension (2D/3D consistency)\n- Compatible flux tensor shapes\n- All models properly initialized", - "harvested_comments": [ - "Validate model is properly initialized" - ], + "returns": "None", + "existing_docstring": "Register a Snapshottable object with this model.\n\nCalled by helper classes (DDt, parameter-history holders, ...)\nin their ``__init__``. ``obj`` should expose a ``.state``\nattribute returning a dataclass with ``_schema_version``.\nMembership is via WeakSet, so registration does not extend\n``obj``'s lifetime.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "MultiMaterialConstitutiveModel", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_lock_units", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 240, + "file": "src/underworld3/model.py", + "line": 1072, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Return the identity tensor of appropriate rank (e.g. for projections)", + "existing_docstring": "Lock the model units to prevent changes after mesh creation.\n\nThis is called automatically when the first mesh is registered.\nOnce locked, calling set_reference_quantities() will raise an error.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Constitutive_Model", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_check_units_locked", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 314, + "file": "src/underworld3/model.py", + "line": 1081, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "For this constitutive law, we expect just a viscosity function", + "existing_docstring": "Check if model units are locked.\n\nRaises:\n-------\nRuntimeError\n If units are locked and user tries to change reference quantities", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "ViscousFlowModel", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_simple_dimensional_analysis", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1301, - "signature": "(self)", + "file": "src/underworld3/model.py", + "line": 1120, + "signature": "(self) -> dict", "parameters": [], - "returns": null, - "existing_docstring": "For this constitutive law, we expect just a viscosity function", + "returns": "dict", + "existing_docstring": "Pure mathematical dimensional analysis using linear algebra.\n\nUses physics (dimensional structure) not linguistics (names) to derive\nfundamental scales. Works with any user terminology.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "ViscoElasticPlasticFlowModel", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_comprehensive_dimensional_analysis", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1516, - "signature": "(self)", + "file": "src/underworld3/model.py", + "line": 1129, + "signature": "(self) -> dict", "parameters": [], - "returns": null, - "existing_docstring": "For this constitutive law, we expect just a diffusivity function", - "harvested_comments": [], - "status": "minimal", + "returns": "dict", + "existing_docstring": "Comprehensive dimensional analysis using linear algebra and Pint.\n\nAnalyzes dimensional coverage, provides intelligent error handling,\nand uses human-friendly formatting. Zero dependency on naming conventions.", + "harvested_comments": [ + "Use the SHARED underworld3 unit registry to avoid registry mismatch errors", + "Build dimensional matrix from pure physics", + "Analyze system properties", + "Handle different system types", + "Always show warnings (parallel-safe)" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "DiffusionModel", + "parent_class": "Model", "is_public": false }, { - "name": "_build_c_tensor", + "name": "_solve_available_dimensions", "kind": "method", - "file": "src/underworld3/constitutive_models_new.py", - "line": 1636, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/model.py", + "line": 1185, + "signature": "(self, matrix, names, fundamental_dims, ureg)", + "parameters": [ + { + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "names", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fundamental_dims", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "ureg", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "For this constitutive law, we expect two viscosity functions\nand a sympy matrix that describes the director components n_{i}", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Solve for fundamental scales from the dimensions available in reference quantities.\n\nDerives scales for whatever dimensions are covered by the user's reference\nquantities (e.g., L, M, T from viscosity, velocity, length). Dimensions not\ncovered are left undefined and will only cause errors if actually requested.\nThis follows Pint's approach: work with what's available.", + "harvested_comments": [ + "Identify which dimensions are covered (have non-zero entries in matrix)", + "Informational message about missing dimensions (not an error!)", + "Extract sub-matrix for covered dimensions only", + "Get magnitudes in SI base units", + "Can we solve the sub-system?" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "TransverseIsotropicFlowModel", + "parent_class": "Model", "is_public": false }, { - "name": "__eq__", + "name": "_solve_complete_system", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 107, - "signature": "(self, other)", + "file": "src/underworld3/model.py", + "line": 1272, + "signature": "(self, matrix, magnitudes, names, fundamental_dims, ureg)", "parameters": [ { - "name": "other", + "name": "matrix", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "magnitudes", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "names", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fundamental_dims", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "ureg", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Equal to the original BaseScalar from the SAME coordinate system.\n\nThis is the key to making sympy.diff() work - when SymPy checks if\nthe differentiation variable matches symbols in the expression,\nthis makes UWCoordinate match the original BaseScalar.\n\nIMPORTANT: We only match BaseScalars from the SAME mesh's coordinate\nsystem using object identity (`is`), not name comparison. This prevents\ncross-mesh coordinate pollution where coordinates from different meshes\nwould be treated as equal due to having the same name \"N.x\".", + "existing_docstring": "Solve complete dimensional system using linear algebra.\n\nGiven n reference quantities with known dimensionalities, solves for the\nfundamental scales [L, T, M, \u03b8] that are consistent with all constraints.\n\nThe system is: matrix @ log10(scales) = log10(magnitudes)\nwhere matrix[i,j] is the power of fundamental dimension j in quantity i.", "harvested_comments": [ - "DON'T fall back to BaseScalar.__eq__ which compares by name!", - "This caused cross-mesh coordinate pollution (issue discovered 2025-12-15)" + "Solve the linear system in log space", + "Create Pint quantities for the fundamental scales - direct, no rounding", + "Verification (only shown if verbose=True)" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "UWCoordinate", + "parent_class": "Model", "is_public": false }, { - "name": "__hash__", + "name": "_create_pint_registry", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 129, + "file": "src/underworld3/model.py", + "line": 1326, "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Hash must match _original_base_scalar's hash for SymPy compatibility.\n\nSince __eq__ returns True when comparing to the original BaseScalar,\nwe MUST return the same hash (Python requirement: a == b \u2192 hash(a) == hash(b)).\n\nThis is critical for SymPy's differentiation to work correctly -\nsympy.diff() uses hash-based lookup to find matching symbols.\n\nNote: Cross-mesh coordinate collision is prevented by __eq__ checking\nobject identity of _original_base_scalar, not by different hashes.", - "harvested_comments": [], - "status": "partial", + "parameters": [], + "returns": null, + "existing_docstring": "Create Pint registry with model-specific _constants.\n\nUses Pint's _constants pattern like _100km for model units.", + "harvested_comments": [ + "Create model-specific registry", + "Store constant definitions for inspection", + "Define multiple aliases for fundamental dimensions", + "ASCII aliases: Easy to type", + "Unicode aliases: Beautiful for display" + ], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": "Model", "is_public": false }, { - "name": "_numpycode", + "name": "_substitute_display_aliases", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 144, - "signature": "(self, printer)", + "file": "src/underworld3/model.py", + "line": 1433, + "signature": "(self, units_str: str) -> str", "parameters": [ { - "name": "printer", - "type_hint": null, + "name": "units_str", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "NumPy code generation for lambdify().\n\nReturns a unique dummy name that lambdify will map to array columns.\nUses the coordinate's internal index to generate a consistent name.", + "returns": "str", + "existing_docstring": "Replace raw constant names with elegant display aliases in unit strings.\n\nExamples:\n\"_6p31e41kg\" \u2192 \"\u2133\"\n\"_1000km\" \u2192 \"\u2112\"\n\"_631152000000000s\" \u2192 \"\ud835\udcaf\"", "harvested_comments": [ - "Use the short coordinate name (x, y, z) based on axis index" + "Replace each constant with its display alias", + "Replace the constant name with display alias" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWCoordinate", + "parent_class": "Model", "is_public": false }, { - "name": "_lambdacode", + "name": "_round_scale_for_conditioning", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 155, - "signature": "(self, printer)", + "file": "src/underworld3/model.py", + "line": 1486, + "signature": "(self, scale: float, reference_mag: float = None) -> float", "parameters": [ { - "name": "printer", - "type_hint": null, + "name": "scale", + "type_hint": "float", "default": null, "description": "" + }, + { + "name": "reference_mag", + "type_hint": "float", + "default": "None", + "description": "" } ], - "returns": null, - "existing_docstring": "Lambda code generation.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "returns": "float", + "existing_docstring": "Round scale to achieve optimal numerical conditioning based on unit_rounding_mode.\n\nThe goal is to choose a scale such that reference_mag / scale falls in a target range:\n- 'powers_of_10': Target [1, 10] (default) - optimal precision\n- 'engineering': Target [1, 1000] - SI prefix friendly, avoids fractions\n\nArgs:\n scale: The derived scale from dimensional analysis\n reference_mag: The reference quantity magnitude to condition (optional)\n\nReturns:\n Rounded scale value\n\nExamples:\n powers_of_10 mode (target result in [1, 10]):\n scale=500, ref=500 \u2192 100 (gives 500/100 = 5.0)\n scale=6.37e6, ref=6.37e6 \u2192 1e6 (gives 6.37e6/1e6 = 6.37)\n\n engineering mode (target result in [1, 1000]):\n scale=500, ref=500 \u2192 1 (gives 500/1 = 500)\n scale=6.37e6, ref=6.37e6 \u2192 1e6 (gives 6.37e6/1e6 = 6.37)", + "harvested_comments": [ + "If no reference magnitude, use old simple rounding", + "Define target range based on mode", + "Engineering: target [1, 1000], use powers of 1000", + "log10(1000)", + "powers_of_10 (default)" ], - "parent_class": "UWCoordinate", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "_pythoncode", + "name": "_round_to_nice_value", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 160, - "signature": "(self, printer)", + "file": "src/underworld3/model.py", + "line": 1561, + "signature": "(self, magnitude: float) -> float", "parameters": [ { - "name": "printer", - "type_hint": null, + "name": "magnitude", + "type_hint": "float", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Python code generation.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "returns": "float", + "existing_docstring": "Simple rounding to nearest power of 10 or 1000 (fallback method).\n\nThis is used when we don't have a reference magnitude to condition against.", + "harvested_comments": [ + "Round to nearest power of 1000", + "powers_of_10", + "Round to nearest power of 10", + "Clean up floating point errors for values >= 1" ], - "parent_class": "UWCoordinate", - "is_public": false - }, - { - "name": "_base_scalar", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 203, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Backward compatibility: return the original BaseScalar.", - "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", - "is_public": false - }, - { - "name": "_ccodestr", - "kind": "property", - "file": "src/underworld3/coordinates.py", - "line": 224, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Delegate C code string to the original BaseScalar.\n\nThe mesh sets _ccodestr on the original N.x, N.y, N.z objects\nfor JIT code generation (e.g., \"petsc_x[0]\", \"petsc_x[1]\").\nUWCoordinate must expose the same attribute for JIT to work.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "UWCoordinate", + "parent_class": "Model", "is_public": false }, { - "name": "_ccodestr", + "name": "_generate_constant_name", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 235, - "signature": "(self, value)", + "file": "src/underworld3/model.py", + "line": 1589, + "signature": "(self, magnitude: float, si_units: str, dimension: str) -> str", "parameters": [ { - "name": "value", - "type_hint": null, + "name": "magnitude", + "type_hint": "float", + "default": null, + "description": "" + }, + { + "name": "si_units", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "dimension", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Allow setting _ccodestr (propagates to original BaseScalar).", - "harvested_comments": [], + "returns": "str", + "existing_docstring": "Generate Pint _constant names following _100km pattern.", + "harvested_comments": [ + "Round to nice value first to avoid floating point precision issues", + "Simplify common SI units", + "Format magnitude for readability - ensure valid Python identifiers", + "IMPORTANT: Avoid using recognizable unit names (like \"km\", \"m\", \"kg\") in constant names!", + "Pint tries to parse them and fails. Use separators to prevent parsing." + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWCoordinate", + "parent_class": "Model", "is_public": false }, { - "name": "_ccode", + "name": "_convert_to_user_time_unit", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 239, - "signature": "(self, printer, **kwargs)", + "file": "src/underworld3/model.py", + "line": 1631, + "signature": "(self, time_base_units, velocity_quantity)", "parameters": [ { - "name": "printer", + "name": "time_base_units", "type_hint": null, "default": null, "description": "" }, { - "name": "**kwargs", + "name": "velocity_quantity", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "C code representation for JIT compilation.\n\nThe SymPy CCodePrinter looks for this method on symbols.\nWe delegate to the original BaseScalar's _ccodestr.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "existing_docstring": "Infer appropriate time unit from velocity reference quantity.\n\nThis implements Stage 2 of the two-stage simplification:\nAfter rationalizing to SI base units, convert to user-friendly time units\nby inferring the appropriate scale from the velocity's unit system.\n\nParameters\n----------\ntime_base_units : pint.Quantity\n Time in SI base units (seconds)\nvelocity_quantity : pint.Quantity\n The velocity quantity used to derive time (contains unit hints)\n\nReturns\n-------\npint.Quantity\n Time in user-appropriate units (year, megayear, day, etc.)\n\nExamples\n--------\n>>> # If velocity is in cm/year, time should be in years or megayears\n>>> time_sec = 1.26e14 * ureg.second\n>>> velocity = 5 * ureg.cm / ureg.year\n>>> result = model._convert_to_user_time_unit(time_sec, velocity)\n>>> # result: 40.0 megayear (not 1.26e14 second)", + "harvested_comments": [ + "If velocity is in cm/year, time should be in years or megayears", + "result: 40.0 megayear (not 1.26e14 second)", + "Infer time unit from velocity's time component", + "Geological time scale", + "Daily time scale" ], - "parent_class": "UWCoordinate", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "_latex", + "name": "_choose_display_units", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 251, - "signature": "(self, printer)", + "file": "src/underworld3/model.py", + "line": 1700, + "signature": "(self, quantity, dimension_name = '[unknown]')", "parameters": [ { - "name": "printer", + "name": "quantity", "type_hint": null, "default": null, "description": "" + }, + { + "name": "dimension_name", + "type_hint": null, + "default": "'[unknown]'", + "description": "" } ], "returns": null, - "existing_docstring": "LaTeX representation for SymPy printing.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Choose magnitude-appropriate units for display.\n\nThis implements the display-time unit selection strategy: choose units\nthat give values between 0.1 and 1000 for better readability.\n\nParameters\n----------\nquantity : pint.Quantity\n Quantity to display in appropriate units\ndimension_name : str, optional\n Dimension name for context (e.g., '[length]', '[time]', '[mass]')\n\nReturns\n-------\npint.Quantity\n Same quantity in magnitude-appropriate units\n\nExamples\n--------\n>>> # Length: 2e6 m \u2192 2000 km\n>>> length = 2e6 * ureg.meter\n>>> display_length = model._choose_display_units(length, '[length]')\n>>> # display_length: 2000 kilometer\n\n>>> # Time: 1.26e14 s \u2192 40 Myr\n>>> time = 1.26e14 * ureg.second\n>>> display_time = model._choose_display_units(time, '[time]')\n>>> # display_time: 40.0 megayear", + "harvested_comments": [ + "Length: 2e6 m \u2192 2000 km", + "display_length: 2000 kilometer", + "Time: 1.26e14 s \u2192 40 Myr", + "display_time: 40.0 megayear", + "Get base magnitude for comparison" ], - "parent_class": "UWCoordinate", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "_invalidate_cache", + "name": "_format_scale_representations", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 548, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Mark coordinate cache as invalid (call when mesh coordinates change).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 2291, + "signature": "(self, scale_qty) -> str", + "parameters": [ + { + "name": "scale_qty", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": false - }, - { - "name": "_compute_coordinates", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 552, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Compute geographic coordinates from Cartesian mesh coordinates.", + "returns": "str", + "existing_docstring": "Format a scale quantity showing three representations:\n1. Derivation form (how it was derived)\n2. SI base units (simplified)\n3. Model base units (what 1.0 represents in the model)\n\nParameters\n----------\nscale_qty : pint.Quantity\n The scale quantity to format\n\nReturns\n-------\nstr\n Formatted string with all three representations", "harvested_comments": [ - "Get raw Cartesian coordinates from DM (avoids unit wrapping)", - "The mesh stores nondimensional coords when units are active", - "Get ellipsoid parameters", - "Use nondimensional if available (when mesh created with units)", - "Otherwise use raw km values" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "1. Derivation form (current complex units)", + "2. SI base units (simplified)", + "3. Model base units (what 1.0 represents)", + "The scale quantity itself represents what \"1.0\" means in model units", + "So we format it in a clear way to show this" ], - "parent_class": "GeographicCoordinateAccessor", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "__getitem__", + "name": "_get_domain_friendly_scale", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 653, - "signature": "(self, idx)", + "file": "src/underworld3/model.py", + "line": 2466, + "signature": "(self, qty)", "parameters": [ { - "name": "idx", + "name": "qty", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Access symbolic geographic coordinates.\n\nReturns:\n \u03bb_lon, \u03bb_lat, \u03bb_d: Symbolic coordinates for use in equations", + "existing_docstring": "Choose domain-appropriate units based on magnitude using Pint's capabilities.", "harvested_comments": [ - "mesh.geo[:]" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "Use the SHARED underworld3 unit registry to avoid registry mismatch errors", + "Length scales - choose appropriate geological/engineering units", + "Time scales - choose appropriate geological/engineering units", + "Mass scales", + "Very large masses" ], - "parent_class": "GeographicCoordinateAccessor", - "is_public": false - }, - { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 866, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "String representation showing available coordinates and basis vectors.", - "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "GeographicCoordinateAccessor", + "parent_class": "Model", "is_public": false }, { - "name": "_invalidate_cache", + "name": "_suggest_reference_quantities", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1008, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Mark coordinate cache as invalid (call when mesh coordinates change).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 2687, + "signature": "(self, missing_dimensions) -> list", + "parameters": [ + { + "name": "missing_dimensions", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SphericalCoordinateAccessor", + "returns": "list", + "existing_docstring": "Suggest specific reference quantities to complete missing dimensions.\n\nParameters\n----------\nmissing_dimensions : list\n List of dimension names that are missing\n\nReturns\n-------\nlist\n List of suggestion strings for each missing dimension", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "_compute_coordinates", + "name": "_handle_conversion_failure", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1012, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/model.py", + "line": 2751, + "signature": "(self, qty, qty_dimensionality)", + "parameters": [ + { + "name": "qty", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "qty_dimensionality", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Compute spherical/polar coordinates from Cartesian mesh coordinates.", + "existing_docstring": "Handle conversion failure with diagnostic error messages and suggestions.\n\nParameters\n----------\nqty : pint.Quantity\n The quantity that failed to convert\nqty_dimensionality : pint.util.UnitsContainer\n The dimensionality of the failed quantity\n\nReturns\n-------\nUWQuantity\n Dimensionless quantity with original magnitude (fallback)\n\nRaises\n------\nValueError\n With diagnostic information when strict error handling is enabled", "harvested_comments": [ - "Get Cartesian coordinates", - "2D polar: (x, y) \u2192 (r, \u03b8)", - "3D spherical: (x, y, z) \u2192 (r, \u03b8, \u03c6)" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "Analyze what dimensions are needed vs. available", + "Convert Pint internal dimension to our naming", + "Get validation results", + "Build diagnostic message", + "For now, issue a warning instead of raising an error for backward compatibility" ], - "parent_class": "SphericalCoordinateAccessor", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "__getitem__", + "name": "_optimize_scales_for_readability", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1100, - "signature": "(self, idx)", + "file": "src/underworld3/model.py", + "line": 2866, + "signature": "(self, scalings)", "parameters": [ { - "name": "idx", + "name": "scalings", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Access symbolic spherical/polar coordinates.\n\nReturns\n-------\ntuple or scalar\n For 3D: r, \u03b8, \u03c6 symbolic coordinates\n For 2D: r, \u03b8 symbolic coordinates", + "existing_docstring": "Optimize scales to use nice round numbers while keeping reference quantities O(1).\n\nParameters\n----------\nscalings : dict\n Dictionary of fundamental scalings from derive_fundamental_scalings()\n\nReturns\n-------\ndict\n Optimized scalings with nice round numbers", "harvested_comments": [ - "mesh.spherical[:]" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" + "Find nearest \"nice\" scale", + "Import units backend to create new quantity", + "Fallback to original scale if optimization fails", + "Non-standard scale format, keep as-is" ], - "parent_class": "SphericalCoordinateAccessor", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "__repr__", + "name": "_find_nice_scale", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1281, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "String representation showing available coordinates and basis vectors.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 2902, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "SphericalCoordinateAccessor", - "is_public": false - }, - { - "name": "_apply_units_scaling", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1805, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Mark coordinate system as scaled if model has units.", + "existing_docstring": "Find nearest 'nice' number (powers of 10 times 1, 2, or 5).\n\nParameters\n----------\nvalue : float\n Original scale value\n\nReturns\n-------\nfloat\n Nearest nice scale value\n\nExamples\n--------\n>>> model._find_nice_scale(2900) # \u2192 1000 or 5000\n>>> model._find_nice_scale(0.38) # \u2192 0.5 or 0.2\n>>> model._find_nice_scale(15.7) # \u2192 10 or 20", "harvested_comments": [ - "Get the model from the mesh", - "Fall back to default model if mesh doesn't have one", - "Check if the model has units scaling enabled", - "No scaling to apply", - "Get fundamental scales from the model" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "\u2192 1000 or 5000", + "\u2192 0.5 or 0.2", + "Get the order of magnitude", + "Choose from nice mantissas: 1, 2, 5", + "Include 10 to handle edge cases" ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "__getitem__", + "name": "_detect_scaling_conflicts", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1861, - "signature": "(self, idx)", + "file": "src/underworld3/model.py", + "line": 2948, + "signature": "(self, ref_qty)", "parameters": [ { - "name": "idx", + "name": "ref_qty", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Support mesh.X[0] for x-coordinate access.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Detect conflicts in over-determined dimensional systems.\n\nParameters\n----------\nref_qty : dict\n Reference quantities dictionary\n\nReturns\n-------\ndict\n Dictionary with keys:\n - 'has_conflicts': bool indicating if conflicts were found\n - 'conflicts': list of conflict descriptions\n - 'resolutions': list of suggested resolutions\n - 'redundant_quantities': list of quantity names that could be removed", + "harvested_comments": [ + "Import units (Pint) for dimensional analysis", + "Build a map of what dimensions each quantity provides", + "Track which quantities can provide each base dimension", + "Check for direct conflicts (multiple quantities of same dimension)", + "Keep first, mark others as redundant" ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": "Model", "is_public": false }, { - "name": "__iter__", + "name": "_convert_to_model_units_general", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1865, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Support x, y = mesh.X unpacking.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "file": "src/underworld3/model.py", + "line": 3523, + "signature": "(self, quantity)", + "parameters": [ + { + "name": "quantity", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "CoordinateSystem", - "is_public": false - }, - { - "name": "__len__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1869, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Support len(mesh.X).", - "harvested_comments": [], + "existing_docstring": "Convert any quantity to model units using Pint's native conversion.\n\nReturns None for dimensionless quantities gracefully.", + "harvested_comments": [ + "NEW APPROACH: Use Pint's native .to() method instead of manual dimensional analysis", + "This avoids double-scaling issues and leverages Pint's unit conversion engine", + "IMPORTANT: The source quantity might be in a different Pint registry!", + "We need to reconstruct it in the MODEL's registry before converting.", + "First get magnitude and units from the input" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "Model", "is_public": false }, { - "name": "_sympy_", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1947, + "file": "src/underworld3/model.py", + "line": 4495, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Tell SymPy how to convert this object to a SymPy expression.\n\nNote: Uses _sympy_() protocol (not _sympify_()) for SymPy 1.14+ compatibility.\nThis is required for proper symbolic algebra in strict mode (matrix operations).\n\nThis enables CoordinateSystem to work seamlessly with SymPy operations\nlike diff, jacobian, and arithmetic operations.", - "harvested_comments": [], - "status": "partial", + "existing_docstring": "Override Pydantic's __repr__ for better user experience.", + "harvested_comments": [ + "Add units information" + ], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "Model", "is_public": false }, { - "name": "__sympy__", + "name": "__str__", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1959, + "file": "src/underworld3/model.py", + "line": 4512, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Alternative SymPy conversion protocol.", + "existing_docstring": "String representation for print() calls.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "Model", "is_public": false }, { - "name": "__getattr__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 1991, - "signature": "(self, name)", + "name": "_should_rank_execute", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 58, + "signature": "(current_rank, rank_selector, total_size)", "parameters": [ { - "name": "name", + "name": "current_rank", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "rank_selector", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "total_size", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Delegate SymPy-specific attributes to the underlying symbolic matrix.\n\nThis allows CoordinateSystem to be used transparently in SymPy operations\nby forwarding attribute access to _X when the attribute doesn't exist\non CoordinateSystem itself.\n\nNote: When properties like 'geo' or 'spherical' raise AttributeError\n(because the coordinate system doesn't support them), Python falls\nthrough to __getattr__. We detect this and re-invoke the property\nto get the helpful error message.", - "harvested_comments": [ - "Prevent infinite recursion for _X access", - "For coordinate accessor properties, re-invoke the property to get", - "the helpful error message (they raise AttributeError with guidance)", - "Access the property descriptor directly to get its error message", - "Try to get the attribute from the underlying symbolic matrix" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" - ], - "parent_class": "CoordinateSystem", + "existing_docstring": "Determine if a rank should execute based on rank selector.\n\nArgs:\n current_rank: The rank to check\n rank_selector: int, slice, list, tuple, callable, str, or numpy array\n total_size: Total number of ranks\n\nReturns:\n bool: True if rank should execute", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__add__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2024, - "signature": "(self, other)", + "name": "_get_executing_ranks", + "kind": "function", + "file": "src/underworld3/mpi.py", + "line": 109, + "signature": "(rank_selector, total_size)", "parameters": [ { - "name": "other", + "name": "rank_selector", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "total_size", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Support mesh.X + other.", + "existing_docstring": "Get set of ranks that will execute for a given selector.\n\nArgs:\n rank_selector: Rank selection specification\n total_size: Total number of ranks\n\nReturns:\n set: Set of rank numbers that will execute", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__radd__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2028, - "signature": "(self, other)", + "name": "_add_planetary_units", + "kind": "function", + "file": "src/underworld3/scaling/_scaling.py", + "line": 24, + "signature": "(registry)", "parameters": [ { - "name": "other", + "name": "registry", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Support other + mesh.X.", - "harvested_comments": [], + "existing_docstring": "Add planetary, geophysical, and astronomical units to the registry.", + "harvested_comments": [ + "=== PLANETARY MASSES ===", + "Inner Solar System", + "Outer Solar System", + "=== PLANETARY RADII ===", + "Inner Solar System" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": false }, { - "name": "__sub__", - "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2032, - "signature": "(self, other)", + "name": "_units_view", + "kind": "function", + "file": "src/underworld3/scaling/_scaling.py", + "line": 370, + "signature": "(registry, verbose: int = 0, show_scaling: bool = True, show_systems: bool = True)", "parameters": [ { - "name": "other", + "name": "registry", "type_hint": null, "default": null, "description": "" + }, + { + "name": "verbose", + "type_hint": "int", + "default": "0", + "description": "" + }, + { + "name": "show_scaling", + "type_hint": "bool", + "default": "True", + "description": "" + }, + { + "name": "show_systems", + "type_hint": "bool", + "default": "True", + "description": "" } ], "returns": null, - "existing_docstring": "Support mesh.X - other.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Display a notebook-friendly summary of the units registry and scaling system.\n\nParameters\n----------\nverbose : int, default 0\n Verbosity level:\n 0 = Basic units and scaling summary\n 1 = Include available unit systems and contexts\n 2 = Include detailed unit listings by dimension\nshow_scaling : bool, default True\n Whether to show current scaling coefficients\nshow_systems : bool, default True\n Whether to show available unit systems\n\nExample\n-------\n>>> uw.scaling.units.view() # Basic summary\n>>> uw.scaling.units.view(verbose=1) # Include systems\n>>> uw.scaling.units.view(verbose=2) # Detailed units listing", + "harvested_comments": [ + "Basic summary", + "Include systems", + "Detailed units listing", + "Build markdown content", + "Units Registry & Scaling System\")" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": null, "is_public": false }, { - "name": "__rsub__", + "name": "_create_variable_array", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2036, - "signature": "(self, other)", + "file": "src/underworld3/swarm.py", + "line": 384, + "signature": "(self, initial_data = None)", "parameters": [ { - "name": "other", + "name": "initial_data", "type_hint": null, - "default": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Support other - mesh.X.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "existing_docstring": "Factory function to create NDArray_With_Callback for variable data.\nFollows the same pattern as swarm.points implementation.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Array object with callback for automatic PETSc synchronization", + "harvested_comments": [ + "Create NDArray_With_Callback (following swarm._points pattern)", + "Allow operations like existing arrays", + "Single callback function (following swarm_update_callback pattern)", + "This guard handles cases where the array is accessed during", + "object teardown (e.g. at application exit or mesh rebuilds)," ], - "parent_class": "CoordinateSystem", + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "__mul__", + "name": "_create_canonical_data_array", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2040, - "signature": "(self, other)", + "file": "src/underworld3/swarm.py", + "line": 441, + "signature": "(self, initial_data = None)", "parameters": [ { - "name": "other", + "name": "initial_data", "type_hint": null, - "default": null, + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Support mesh.X * other.", + "existing_docstring": "Create the single canonical data array with PETSc synchronization.\nThis is the ONLY method that creates arrays with PETSc callbacks.\n\nReturns data in shape (-1, num_components) using pack_raw/unpack_raw methods.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Canonical array object with callback for automatic PETSc synchronization", + "harvested_comments": [ + "Use unpack_raw to get flat format (-1, num_components)", + "Handle case where unpack returns None (swarm not initialized)", + "Create NDArray_With_Callback for flat data", + "Allow operations like existing arrays", + "Single canonical callback for PETSc synchronization" + ], + "status": "complete", + "needs": [], + "parent_class": "SwarmVariable", + "is_public": false + }, + { + "name": "_create_array_view", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 526, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Create array view of canonical data using appropriate conversion strategy.\n\nStrategy depends on variable complexity:\n- Scalars/Vectors: Simple reshape operations\n- 2D+ Tensors: Complex pack/unpack operations\n\nReturns\n-------\nArrayView\n Array-like object that delegates changes back to canonical data", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "__rmul__", + "name": "_is_simple_variable", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2044, - "signature": "(self, other)", - "parameters": [ - { - "name": "other", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 544, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Support other * mesh.X.", + "existing_docstring": "Check if this is a simple scalar/vector variable (not a complex tensor)", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "__truediv__", + "name": "_create_simple_array_view", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2048, - "signature": "(self, other)", - "parameters": [ - { - "name": "other", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 548, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Support mesh.X / other.", - "harvested_comments": [], + "existing_docstring": "Array view for scalars/vectors using simple reshape operations", + "harvested_comments": [ + "Simple reshape: (-1, num_components) -> (N, a, b)", + "For simple variables, reshape to (N, a, b) format", + "Apply dimensionalization if needed", + "Check if variable has units and model has reference quantities", + "Variable has units - wrap with UnitAwareArray" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "__rtruediv__", + "name": "_create_tensor_array_view", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2052, - "signature": "(self, other)", - "parameters": [ - { - "name": "other", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 761, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Support other / mesh.X.", - "harvested_comments": [], + "existing_docstring": "Array view for complex tensors using pack/unpack operations", + "harvested_comments": [ + "Use complex pack/unpack for tensor layouts", + "Apply dimensionalization if needed", + "Check if variable has units and model has reference quantities", + "Variable has units - wrap with UnitAwareArray", + "If ND scaling is active, data is non-dimensional and needs dimensionalization" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "__pow__", + "name": "_pack_array_to_data_format", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2056, - "signature": "(self, other)", + "file": "src/underworld3/swarm.py", + "line": 972, + "signature": "(self, array_data)", "parameters": [ { - "name": "other", + "name": "array_data", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Support mesh.X ** other.", - "harvested_comments": [], + "existing_docstring": "Convert array format (N,a,b) back to canonical data format (N,components)", + "harvested_comments": [ + "Use existing pack logic but return numpy array instead of writing to PETSc", + "This is a pure conversion method - no PETSc access", + "Empty-partition guard: an N=0 array has total size 0, so numpy cannot", + "infer the -1 component dimension (\"cannot reshape array of size 0 into", + "shape (0,newaxis)\"). This bites a rank that owns no local particles" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "__neg__", + "name": "_update", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2060, + "file": "src/underworld3/swarm.py", + "line": 1086, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Support -mesh.X.", - "harvested_comments": [], + "existing_docstring": "Mark proxy mesh variable as stale for lazy evaluation.\nThe actual update happens when the proxy is accessed.", + "harvested_comments": [ + "if not proxied, nothing to do. return.", + "Mark proxy as stale for lazy evaluation (avoids immediate PETSc access conflicts)" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "_cartesian_to_natural_coords", + "name": "_update_proxy_if_stale", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2528, - "signature": "(self, cartesian_coords)", - "parameters": [ - { - "name": "cartesian_coords", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 1106, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Convert Cartesian coordinates to natural coordinate system.\n\nParameters\n----------\ncartesian_coords : numpy.ndarray\n Array of Cartesian coordinates (N_points, dim)\n\nReturns\n-------\nnumpy.ndarray\n Array of natural coordinates (N_points, dim)", + "existing_docstring": "Actually update the proxy mesh variable if it's marked as stale.\nThis implements lazy evaluation to avoid PETSc access conflicts.", "harvested_comments": [ - "For Cartesian, natural coordinates are the same as Cartesian", - "Convert (x, y) to (r, theta)", - "Convert (x, y, z) to (r, theta, z)", - "Convert (x, y, z) to (r, theta, phi)", - "colatitude (0 to pi)" + "if not proxied, nothing to do. return.", + "Only update if stale and not already updating", + "Lifetime contract: variables hold their parent swarm by WEAK", + "reference (a strong back-reference would cycle with the swarm's", + "own strong _coord_var/_X0 members and defer DMSwarm destruction" ], - "status": "complete", - "needs": [], - "parent_class": "CoordinateSystem", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "_create_cartesian_profile", + "name": "_rbf_to_meshVar", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2622, - "signature": "(self, profile_type, **params)", + "file": "src/underworld3/swarm.py", + "line": 1152, + "signature": "(self, meshVar, nnn = None, verbose = False)", "parameters": [ { - "name": "profile_type", + "name": "meshVar", "type_hint": null, "default": null, "description": "" }, { - "name": "**params", + "name": "nnn", "type_hint": null, - "default": null, + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Create profiles for Cartesian coordinate systems", + "existing_docstring": "Here is how it works: for each particle, create a distance-weighted average on the node data\n\nTodo: caching the k-d trees etc for the proxy-mesh-variable nodal points\nTodo: some form of global fall-back for when there are no particles on a processor", "harvested_comments": [ - "Horizontal line at specified y-position", - "Same for Cartesian", - "Vertical line at specified x-position", - "Diagonal line from start to end point" + "Mapping to the coordinates of the variable from the", + "particle coords", + "If this is our own proxy variable and mesh has changed, recreate it", + "Use the newly created proxy variable", + "Starved-rank guard (SWARM-07): with <= 1 local particles there is" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "_create_cylindrical_profile", + "name": "_rbf_reduce_to_meshVar", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2695, - "signature": "(self, profile_type, **params)", + "file": "src/underworld3/swarm.py", + "line": 1208, + "signature": "(self, meshVar, verbose = False)", "parameters": [ { - "name": "profile_type", + "name": "meshVar", "type_hint": null, "default": null, "description": "" }, { - "name": "**params", + "name": "verbose", "type_hint": null, - "default": null, + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Create profiles for cylindrical coordinate systems", + "existing_docstring": "This method updates a mesh variable for the current\nswarm & particle variable state by reducing the swarm to\nthe nearest point for each particle\n\nHere is how it works:\n\n 1) for each particle, create a distance-weighted average on the node data\n 2) check to see which nodes have zero weight / zero contribution and replace with nearest particle value\n\nTodo: caching the k-d trees etc for the proxy-mesh-variable nodal points\nTodo: some form of global fall-back for when there are no particles on a processor", "harvested_comments": [ - "Radial line at specified angle", - "Angle in radians", - "Convert to Cartesian coordinates", - "Natural coordinates", - "Tangential (circular arc) at specified radius" + "if not proxied, nothing to do. return.", + "1 - Average particles to nodes with distance weighted average", + "Use cached KDTree for interpolation (avoids redundant index construction)", + "need actual distances", + "2 - set NN vals on mesh var where w == 0.0" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "_create_spherical_profile", + "name": "_warn_deprecated_sync", "kind": "method", - "file": "src/underworld3/coordinates.py", - "line": 2763, - "signature": "(self, profile_type, **params)", + "file": "src/underworld3/swarm.py", + "line": 1275, + "signature": "(sync)", "parameters": [ { - "name": "profile_type", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "**params", + "name": "sync", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create profiles for spherical coordinate systems", + "existing_docstring": "One-cycle keyword shim for the removed no-op ``sync=`` argument on\nthe four pack/unpack methods (it never had an effect).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SwarmVariable", + "is_public": false + }, + { + "name": "_object_viewer", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 1470, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "This will substitute specific information about this object", "harvested_comments": [ - "Radial line at specified theta, phi", - "Colatitude (0 to pi)", - "Azimuth (-pi to pi)", - "Convert to Cartesian coordinates", - "Natural coordinates" + "feedback on this instance" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "CoordinateSystem", + "parent_class": "SwarmVariable", "is_public": false }, { - "name": "_from_gmsh", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 47, - "signature": "(filename, comm = None, markVertices = False, useRegions = True, useMultipleTags = True)", - "parameters": [ - { - "name": "filename", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "comm", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "markVertices", - "type_hint": null, - "default": "False", - "description": "" - }, - { - "name": "useRegions", - "type_hint": null, - "default": "True", - "description": "" - }, - { - "name": "useMultipleTags", - "type_hint": null, - "default": "True", - "description": "" - } + "name": "_update", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2248, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Backward compatibility wrapper for _update_proxy_variables.\n\nMaintains existing API while implementing lazy evaluation internally.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "IndexSwarmVariable", + "is_public": false + }, + { + "name": "_on_data_changed", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2256, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Read a Gmsh .msh file from `filename`.\n\n:kwarg comm: Optional communicator to build the mesh on (defaults to\n COMM_WORLD).", - "harvested_comments": [ - "# NOTE: - this should be smart enough to serialise the msh conversion", - "# and then read back in parallel via h5. This is currently done", - "# by every gmesh mesh", - "This option allows objects to be in multiple physical groups", - "Rather than just the first one found." - ], + "existing_docstring": "Hook called by unified data callback when canonical data changes.\n\nFor IndexSwarmVariable, this marks proxy variables as stale for lazy evaluation.\nThis replaces the complex custom array override with a simple hook.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "IndexSwarmVariable", "is_public": false }, { - "name": "_from_plexh5", - "kind": "function", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 117, - "signature": "(filename, comm = None, return_sf = False)", - "parameters": [ - { - "name": "filename", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "comm", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "return_sf", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "name": "_update_proxy_if_stale", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2265, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Read a dmplex .h5 file from `filename` provided.\n\ncomm: Optional communicator to build the mesh on (defaults to\nCOMM_WORLD).", + "existing_docstring": "Refresh the level-set proxy variables if they are marked stale.\n\nOverrides the base implementation (which requires ``self._meshVar``;\nan IndexSwarmVariable keeps its proxies in ``_meshLevelSetVars``\ninstead). Used by the lazy ``.sym`` accessor and by the solve-entry\nrefresh (``Swarm._sync_before_assembly``).", "harvested_comments": [ - "h5plex = PETSc.DMPlex().createFromFile(filename, comm=comm)", - "Do this as well" + "Fossil-variable contract \u2014 see the base implementation: a", + "variable that outlives its (weakly-referenced) parent swarm keeps", + "the last level-set projection and warns instead of raising.", + "nothing can ever refresh it again" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": null, + "parent_class": "IndexSwarmVariable", "is_public": false }, { - "name": "_deform_mesh", + "name": "_update_proxy_variables", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1241, - "signature": "(self, new_coords: numpy.ndarray, verbose = False)", - "parameters": [ - { - "name": "new_coords", - "type_hint": "numpy.ndarray", - "default": null, - "description": "" - }, - { - "name": "verbose", - "type_hint": null, - "default": "False", - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 2452, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "This method will update the mesh coordinates and reset any cached coordinates in\nthe mesh and in equation systems that are registered on the mesh.\n\nThe coord array that is passed in should match the shape of self.data", - "harvested_comments": [], + "existing_docstring": "This method updates the proxy mesh (vector) variable for the index variable on the current swarm locations\n\nHere is how it works:\n\n 1) for each particle, create a distance-weighted average on the node data\n 2) for each index in the set, we create a mask mesh variable by mapping 1.0 wherever the\n index matches and 0.0 where it does not.\n\nNOTE: If no material is identified with a given nodal value, the default is to impose\na near-neighbour hunt for a valid material and set that one\n\n## ToDo: This should be revisited to match the updated master copy of _update\n\nupdate_type 0: assign the particles to the nearest mesh_levelset nodes, and calculate the value on nodes from them.\nupdate_type 1: calculate the material property value on mesh_levelset nodes from the nearest N particles directly.", + "harvested_comments": [ + "# ToDo: This should be revisited to match the updated master copy of _update", + "Starved-rank guard (SWARM-07): with <= 1 local particles the", + "nearest-neighbour machinery cannot run \u2014 KDTree construction on an", + "empty coordinate array raises IndexError, aborting/hanging the", + "collective proxy update \u2014 and there is nothing to project anyway." + ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "IndexSwarmVariable", "is_public": false }, { - "name": "_legacy_access", + "name": "__del__", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 1258, - "signature": "(self, *writeable_vars)", - "parameters": [ - { - "name": "*writeable_vars", - "type_hint": "'MeshVariable'", - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 2804, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "This context manager makes the underlying mesh variables data available to\nthe user. The data should be accessed via the variables `data` handle.\n\nAs default, all data is read-only. To enable writeable data, the user should\nspecify which variable they wish to modify.\n\nParameters\n----------\nwriteable_vars\n The variables for which data write access is required.\n\nExample\n-------\n>>> import underworld3 as uw\n>>> someMesh = uw.discretisation.FeMesh_Cartesian()\n>>> with someMesh._deform_mesh():\n... someMesh.data[0] = [0.1,0.1]\n>>> someMesh.data[0]\narray([ 0.1, 0.1])", + "existing_docstring": "Cleanup swarm: unregister from mesh and model, destroy the DM.\n\nThree steps: drop the mesh-side weak-set entry, drop the model-side\nregistry entry (which also forgets any SwarmVariables that belonged\nto this swarm), and call ``self.dm.destroy()`` to release the PETSc\nDMSwarm and its registered fields.\n\nHistorically the model registry kept a strong reference to every\nswarm and ``__del__`` did not call ``dm.destroy()`` \u2014 both leaks\naccumulated quickly inside time-stepping loops that build transient\nswarms (global expression evaluation, checkpoint reads, mesh\nadaptation transfers). The registry is now a ``WeakValueDictionary``\nand ``__del__`` cleans up its own resources.", "harvested_comments": [ - "Invalidate DMInterpolation cache when DM structure changes", - "if already accessed within higher level context manager, continue.", - "set flag so variable status can be known elsewhere", - "add to de-access list to rewind this later", - "create & set vec" + "Mesh/Model may have already been garbage collected, which is fine", + "DM may already have been destroyed (e.g. by Mesh.adapt) or", + "PETSc may be finalising. Either way, nothing more we can do." ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Swarm", "is_public": false }, { - "name": "_get_coords_for_var", + "name": "_invalidate_canonical_data", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2035, - "signature": "(self, var)", - "parameters": [ - { - "name": "var", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 2839, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "This function returns the vertex array for the\nprovided variable. If the array does not already exist,\nit is first created and then returned.", + "existing_docstring": "Drop cached array views on the coordinates and every registered variable.\n\nRequired after any operation that bypasses :meth:`Swarm.migrate` but\nstill mutates particle layout \u2014 notably the bare ``self.dm.migrate(...)``\nused by round-trip evaluation patterns. Calling :meth:`Swarm.migrate`\nalready does this internally; call this directly only when you used the\nunderlying DMSwarm migrate.", "harvested_comments": [ - "if array already created, return." + "``self._vars`` may be a WeakValueDictionary whose values disappear", + "asynchronously during GC; iterate a snapshot.", + "Mark the proxy stale (lazy) so it re-interpolates on next access.", + "NB: set the flag directly rather than calling var._update() \u2014", + "IndexSwarmVariable._update() is EAGER (_update_proxy_variables)," ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Swarm", "is_public": false }, { - "name": "_get_coords_for_basis", + "name": "_flush_pending_petsc_sync", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2050, - "signature": "(self, degree, continuous)", - "parameters": [ - { - "name": "degree", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "continuous", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/swarm.py", + "line": 2868, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "This function returns the vertex array for the\nprovided variable. If the array does not already exist,\nit is first created and then returned.", - "harvested_comments": [], + "existing_docstring": "Pack canonical arrays written while migration was suppressed.\n\nWrites made inside ``migration_control()`` / ``migration_disabled()``\nland in each variable's canonical array but their PETSc pack is\ndeferred (see the sync callbacks). This flushes them into the DMSwarm\nfields. Called on migration-context exit and defensively at\n:meth:`migrate` entry; a no-op when nothing is pending (SWARM-04).", + "harvested_comments": [ + "cache was invalidated after the write; PETSc already holds", + "the authoritative data \u2014 nothing left to flush." + ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Swarm", "is_public": false }, { - "name": "_mark_faces_inside_and_out", + "name": "_sync_before_assembly", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2256, + "file": "src/underworld3/swarm.py", + "line": 2901, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Create a collection of control point pairs that are slightly inside\nand slightly outside each mesh face (mirrors to each other). This\nallows a fast lookup of whether we on the inside or outside of the plane\ndefined by a face (i.e. same side or other side as the cell centroid). If we are inside\nfor all faces in a convex polyhedron, then we are inside the cell.\n\nInternal Coordinate System Access Pattern\n------------------------------------------\nThis method uses `self._coords` (raw PETSc array) instead of `self.data`\nor `self.X.coords` (unit-wrapped properties) for performance and correctness:\n\n1. **Guard at boundaries**: External interfaces use unit-aware properties\n2. **Raw access internally**: Internal geometric calculations use `self._coords`\n3. **Performance**: Avoids UnitAwareArray overhead in tight loops\n4. **Correctness**: Prevents unit conversion issues in geometric operations\n\nThis is the recommended pattern for internal mesh operations that manipulate\ncoordinates directly.", + "existing_docstring": "Collective: bring PETSc-facing swarm state up to date for a solve.\n\nCalled from ``Mesh.update_lvec()`` \u2014 the common entry point where\nassembly pulls variable data \u2014 this performs, in order:\n\n1. any DEFERRED particle migration (coordinates written through the\n modern interface mark ``_needs_migration`` instead of migrating\n per-write, which would deadlock under uneven writes \u2014 SWARM-03);\n2. an eager refresh of stale proxy mesh variables, which are\n otherwise refreshed only via the lazy ``.sym`` accessor \u2014 solvers\n read the proxy DM directly and previously consumed stale data\n (issue #215 Bug 3 / issue #289).\n\nRank-local flags are combined with a global MAX reduction so every\nrank takes the same sequence of collective actions even when writes\nwere rank-uneven. Repeated calls are no-ops (flag-guarded).", "harvested_comments": [ - "def mesh_face_skeleton_kdtree(mesh):", - "All elements in our mesh are a single type", - "Use raw internal array for internal mesh operations (avoid unit-aware wrapping)", - "Use raw internal array for internal mesh operations (avoid unit-aware wrapping)", - "Compute face normal from point coordinates (already plain numpy arrays)" + "215 Bug 3 / issue #289).", + "A migration-suppressed context is active (SPMD-consistent by", + "construction); leave everything for its exit to handle.", + "A swarm with no particles anywhere has nothing to migrate or", + "project \u2014 leave proxies stale (they refresh after population)" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Swarm", "is_public": false }, { - "name": "_test_if_points_in_cells_internal", + "name": "_get_kdtree", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2351, - "signature": "(self, points, cells)", - "parameters": [ - { - "name": "points", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "cells", - "type_hint": null, - "default": null, - "description": "" - } + "file": "src/underworld3/swarm.py", + "line": 2962, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Return a cached KDTree for the swarm particle coordinates.\nInvalidated automatically whenever particles migrate or positions change.", + "harvested_comments": [ + "Note: self.data returns unit-aware array if units are active,", + "but kdtree construction expects non-dimensional values.", + "Use _particle_coordinates.data directly." + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "Swarm", + "is_public": false + }, + { + "name": "_route_by_nearest_centroid", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 2975, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Determine if the given points lie in the suggested cells.\nUses a mesh skeletonization array to determine whether the point is\nwith the convex polygon / polyhedron defined by a cell.\n\nExact if applied to a linear mesh, approximate otherwise.\n\nParameters\n----------\npoints : array-like\n Coordinate array in any physical unit system (will be auto-converted)\ncells : array-like\n Cell indices to test", + "existing_docstring": "Migrate every particle to the rank whose domain-centroid is closest.\n\nThis is a deterministic alternative to :meth:`migrate`: the destination\nis a pure function of the coordinate, computed identically on every\nrank. Two swarms migrated this way are guaranteed to place equal\ncoordinates on the same rank \u2014 which the standard ``migrate`` does not\nguarantee at partition boundaries (vertex DOFs sitting on a shared face\ncan return ``True`` from ``points_in_domain`` on multiple ranks).\n\nUsed by checkpoint readers and any consumer that needs source data and\nquery coordinates to converge on the same rank without relying on\nPETSc's DOF distribution.", "harvested_comments": [ - "Internal version - points assumed to already be in model units" + "``DMSwarm_rank`` shape varies by PETSc version: 1-D ``(N,)`` on", + "3.21, 2-D ``(N, 1)`` on 3.25+. ``reshape(-1)`` flattens either", + "form into a writable view into the same buffer." ], "status": "partial", "needs": [ + "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Swarm", "is_public": false }, { - "name": "_mark_local_boundary_faces_inside_and_out", + "name": "_force_migration_after_mesh_change", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2390, + "file": "src/underworld3/swarm.py", + "line": 3777, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Create a collection of control point pairs that are slightly inside\nand slightly outside each boundary-defining face (mirrors to each other). This\nallows a fast lookup of whether we on the inside or outside of the domain.\nWe cannot ensure convexity, so this is approximate when close to the boundary", + "existing_docstring": "Force migration of swarm particles after mesh coordinate changes.\n\nThis method bypasses the normal migration_disabled check since mesh\ncoordinate changes require swarm particles to be re-distributed\nregardless of migration disabled state.", "harvested_comments": [ - "Use raw array for internal calculations", - "3D simplex case (probably also OK for hexes)", - "Control points near centroid", - "Control points closer to face nodes" + "Temporarily override migration disabled state", + "Disable variable array callbacks during migration to prevent corruption", + "Collect all variable arrays and disable their callbacks", + "Perform standard migration", + "Re-enable variable array callbacks" ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "Mesh", + "parent_class": "Swarm", "is_public": false }, { - "name": "_get_closest_local_cells_internal", + "name": "_snapshot_stable_name", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2573, - "signature": "(self, coords: numpy.ndarray) -> numpy.ndarray", + "file": "src/underworld3/swarm.py", + "line": 4446, + "signature": "(self) -> str", + "parameters": [], + "returns": "str", + "existing_docstring": "Per-process stable name. ``instance_number`` comes from uw_object.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Swarm", + "is_public": false + }, + { + "name": "_legacy_access", + "kind": "method", + "file": "src/underworld3/swarm.py", + "line": 4597, + "signature": "(self, *writeable_vars)", "parameters": [ { - "name": "coords", - "type_hint": "numpy.ndarray", + "name": "*writeable_vars", + "type_hint": "SwarmVariable", "default": null, "description": "" } ], - "returns": "numpy.ndarray", - "existing_docstring": "This method uses a kd-tree algorithm to find the closest\ncells to the provided coords. For a regular mesh, this should\nbe exactly the owning cell, but if the mesh is deformed, this\nis not guaranteed. Also compares the distance from the cell to the\npoint - if this is larger than the \"cell size\" then returns -1\n\nParameters:\n-----------\ncoords:\n An array of the coordinates for which we wish to determine the\n closest cells. This should be a 2-dimensional array of\n shape (n_coords,dim) in any physical unit system (will be auto-converted).\n\nReturns:\n--------\nclosest_cells:\n An array of indices representing the cells closest to the provided\n coordinates. This will be a 1-dimensional array of\n shape (n_coords).", + "returns": null, + "existing_docstring": "This context manager makes the underlying swarm variables data available to\nthe user. The data should be accessed via the variables `data` handle.\n\nAs default, all data is read-only. To enable writeable data, the user should\nspecify which variable they wish to modify.\n\nAt the conclusion of the users context managed block, numerous further operations\nwill be automatically executed. This includes swarm parallel migration routines\nwhere the swarm's `particle_coordinates` variable has been modified. The swarm\nvariable proxy mesh variables will also be updated for modifed swarm variables.\n\nParameters\n----------\nwriteable_vars\n The variables for which data write access is required.\n\nExample\n-------\n\n>>> import underworld3 as uw\n>>> someMesh = uw.discretisation.FeMesh_Cartesian()\n>>> with someMesh._deform_mesh():\n... someMesh.data[0] = [0.1,0.1]\n>>> someMesh.data[0]\narray([ 0.1, 0.1])", "harvested_comments": [ - "Internal version - coords assumed to already be in model units", - "Create index if required", - "We need to filter points that lie outside the mesh but", - "still are allocated a nearby element by this distance-only check.", - "Part 2 - try to find the lost points by walking nearby cells" + "if already accessed within higher level context manager, continue.", + "set flag so variable status can be known elsewhere", + "add to de-access list to rewind this later", + "grab numpy object, setting read only if necessary", + "increment variable state" ], - "status": "complete", - "needs": [], - "parent_class": "Mesh", + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "Swarm", "is_public": false }, { - "name": "_get_mesh_sizes", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2718, - "signature": "(self, verbose = False)", + "name": "_as_float", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 166, + "signature": "(value)", "parameters": [ { - "name": "verbose", + "name": "value", "type_hint": null, - "default": "False", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Obtain the (local) mesh radii and centroids using kdtree distances\nThis routine is called when the mesh is built / rebuilt", - "harvested_comments": [ - "Use raw internal array for internal mesh operations (avoid unit-aware wrapping)" - ], + "existing_docstring": "Extract a plain float from various numeric types (Pint, UWQuantity, etc.).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Mesh", + "parent_class": null, "is_public": false }, { - "name": "_get_mesh_centroids", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2752, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Obtain and cache the (local) mesh centroids using underworld swarm technology.\nThis routine is called when the mesh is built / rebuilt\n\nThe global cell number corresponding to a centroid is (supposed to be)\nself.dm.getCellNumbering().array.min() + index", - "harvested_comments": [ - ") = petsc_discretisation.petsc_fvm_get_local_cell_sizes(self)" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "name": "_bdf_coefficients", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 182, + "signature": "(order, dt_current, dt_history)", + "parameters": [ + { + "name": "order", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dt_current", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dt_history", + "type_hint": null, + "default": null, + "description": "" + } ], - "parent_class": "Mesh", - "is_public": false - }, - { - "name": "_increment_mesh_version", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh.py", - "line": 2906, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Manually increment mesh version to notify swarms of coordinate changes.\nThis is called automatically when mesh.points is modified, but can be\ncalled manually if coordinates are changed through other means.", - "harvested_comments": [], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "existing_docstring": "Compute BDF coefficients, handling variable timesteps.\n\nFor uniform timesteps the classical BDF-k coefficients are returned\nas exact ``sympy.Rational`` values. When the timestep ratio differs\nfrom unity the variable-step BDF-2 formula is used (BDF-3 falls back\nto variable-step BDF-2 when the ratio exceeds 5 %).\n\nParameters\n----------\norder : int\n BDF order (1, 2, or 3).\ndt_current : float or None\n Current timestep :math:`\\Delta t_n`.\ndt_history : list\n Previous timesteps ``[dt_{n-1}, dt_{n-2}, ...]``.\n\nReturns\n-------\nlist of sympy.Basic\n Coefficients ``[c0, c1, c2, ...]`` such that\n\n .. math::\n\n \\Delta\\psi_{\\mathrm{BDF}} = c_0\\,\\psi^{n+1}\n + c_1\\,\\psi^n + c_2\\,\\psi^{n-1} + \\cdots\n\nNotes\n-----\n**BDF-1** (backward Euler) always returns ``[1, -1]``.\n\n**BDF-2** with variable step uses\n:math:`r = \\Delta t_n / \\Delta t_{n-1}`:\n\n.. math::\n\n c_0 = \\frac{1+2r}{1+r}, \\quad\n c_1 = -(1+r), \\quad\n c_2 = \\frac{r^2}{1+r}\n\nwhich reduces to :math:`[3/2,\\;-2,\\;1/2]` when :math:`r=1`.\n\n**BDF-3** uses the constant-step formula when all ratios are within\n5 % of unity; otherwise it falls back to variable-step BDF-2.", + "harvested_comments": [ + "Exactly constant dt \u2014 use exact rationals", + "Variable dt", + "No usable history \u2014 constant-dt fallback", + "Approximately constant dt \u2014 standard BDF-3", + "Variable dt \u2014 fall back to BDF-2 with variable coefficients" ], - "parent_class": "Mesh", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__new__", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 102, - "signature": "(cls, varname: Union[str, list], mesh: 'Mesh', num_components: Union[int, tuple] = None, vtype: Optional['uw.VarType'] = None, degree: int = 1, continuous: bool = True, varsymbol: Union[str, list] = None, _register: bool = True, units: Optional[str] = None, units_backend: Optional[str] = None)", + "name": "_create_coefficients", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 312, + "signature": "(order, prefix, instance_id)", "parameters": [ { - "name": "varname", - "type_hint": "Union[str, list]", + "name": "order", + "type_hint": null, "default": null, "description": "" }, { - "name": "mesh", - "type_hint": "'Mesh'", + "name": "prefix", + "type_hint": null, "default": null, "description": "" }, { - "name": "num_components", - "type_hint": "Union[int, tuple]", - "default": "None", + "name": "instance_id", + "type_hint": null, + "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Create UWexpression objects for BDF or AM coefficients.\n\nParameters\n----------\norder : int\n Maximum order (number of history terms). Creates order+1 coefficients.\nprefix : str\n LaTeX prefix for display (e.g. \"c^{BDF}\" or \"a^{AM}\").\ninstance_id : int\n Unique ID to disambiguate coefficients from different DDt instances.\n\nReturns\n-------\nlist of UWexpression\n Coefficient expressions initialised to 0.0.", + "harvested_comments": [], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_update_bdf_values", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 341, + "signature": "(coeffs, effective_order, dt, dt_history)", + "parameters": [ { - "name": "vtype", - "type_hint": "Optional['uw.VarType']", - "default": "None", + "name": "coeffs", + "type_hint": null, + "default": null, "description": "" }, { - "name": "degree", - "type_hint": "int", - "default": "1", + "name": "effective_order", + "type_hint": null, + "default": null, "description": "" }, { - "name": "continuous", - "type_hint": "bool", - "default": "True", + "name": "dt", + "type_hint": null, + "default": null, "description": "" }, { - "name": "varsymbol", - "type_hint": "Union[str, list]", - "default": "None", + "name": "dt_history", + "type_hint": null, + "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Update BDF coefficient UWexpression values for current state.\n\nSets active coefficients from _bdf_coefficients() and zeroes the rest.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_update_am_values", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 353, + "signature": "(coeffs, effective_order, theta = 0.5)", + "parameters": [ { - "name": "_register", - "type_hint": "bool", - "default": "True", + "name": "coeffs", + "type_hint": null, + "default": null, "description": "" }, { - "name": "units", - "type_hint": "Optional[str]", - "default": "None", + "name": "effective_order", + "type_hint": null, + "default": null, "description": "" }, { - "name": "units_backend", - "type_hint": "Optional[str]", - "default": "None", + "name": "theta", + "type_hint": null, + "default": "0.5", "description": "" } ], "returns": null, - "existing_docstring": "Create or return existing MeshVariable instance.\n\nHandles object uniqueness and mesh DM state management.", - "harvested_comments": [ - "# Check if already defined (return existing object)", - "NOTE: DM reconstruction is now handled in _setup_ds() - no snapshotting needed here", - "Create new instance", - "Store parameters for __init__" - ], - "status": "minimal", + "existing_docstring": "Update Adams-Moulton coefficient UWexpression values for current state.\n\nAM coefficients for each order (constant-dt formulas):\n- Order 0: [1]\n- Order 1: [theta, 1-theta]\n- Order 2: [5/12, 8/12, -1/12]\n- Order 3: [9/24, 19/24, -5/24, 1/24]", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_create_variable_array", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 455, - "signature": "(self, initial_data = None)", + "name": "_create_exp_coefficients", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 377, + "signature": "(instance_id)", "parameters": [ { - "name": "initial_data", + "name": "instance_id", "type_hint": null, - "default": "None", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Factory function to create NDArray_With_Callback for variable data.\nFollows the same pattern as mesh.points implementation.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Array object with callback for automatic PETSc synchronization", - "harvested_comments": [ - "Create NDArray_With_Callback (following mesh._points pattern)", - "Allow operations like existing arrays", - "Single callback function (following mesh_update_callback pattern)", - "Only act on data-changing operations (following mesh.points pattern)", - "Prevent recursion by checking if we're already in a callback" + "existing_docstring": "Create UWexpression objects for the ETD-2 coefficients ``[\u03b1, \u03c6]``.\n\nThese are named to render as ``\u03b1_exp`` and ``\u03c6_exp`` (with the\nDDt instance id appended) so they remain visually distinct from\nBDF/AM coefficients in symbolic output.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_create_flat_data_array", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 518, - "signature": "(self, initial_data = None)", + "name": "_update_exp_values", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 399, + "signature": "(coeffs, dt, tau_eff)", "parameters": [ { - "name": "initial_data", + "name": "coeffs", "type_hint": null, - "default": "None", + "default": null, + "description": "" + }, + { + "name": "dt", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tau_eff", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Factory function to create NDArray_With_Callback for backward-compatible flat data.\nReturns data in shape (-1, num_components) using pack_raw/unpack_raw methods.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Array object with callback for automatic PETSc synchronization", + "existing_docstring": "Update exponential-integrator coefficient values for current state.\n\nComputes :math:`\\alpha = e^{-\\Delta t/\\tau_\\mathrm{eff}}` and\n:math:`\\varphi = (1-\\alpha)/(\\Delta t/\\tau_\\mathrm{eff})` and stores\nthem in ``coeffs[0]``, ``coeffs[1]`` respectively. The viscous limit\n(:math:`\\Delta t/\\tau \\to \\infty`) gives ``\u03b1=0, \u03c6=0``; the elastic\nlimit (:math:`\\Delta t/\\tau \\to 0`) gives ``\u03b1=1, \u03c6=1``.\n\nParameters\n----------\ncoeffs : list of UWexpression\n Two-element list ``[\u03b1, \u03c6]`` to update.\ndt : float or None\n Current timestep.\ntau_eff : float or None\n Maxwell relaxation time (\u03b7_eff/\u03bc). When None or non-positive,\n defaults to the viscous limit.", "harvested_comments": [ - "Use unpack_raw to get flat format (-1, num_components)", - "Create NDArray_With_Callback for flat data", - "Allow operations like existing arrays", - "Callback for flat data format", - "Only act on data-changing operations" + "viscous limit", + "elastic limit", + "well-defined small phi, exact for large x" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_build_weighted_sum", + "kind": "function", + "file": "src/underworld3/systems/ddt.py", + "line": 436, + "signature": "(coeffs, psi_fn, psi_star_syms)", + "parameters": [ + { + "name": "coeffs", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "psi_fn", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "psi_star_syms", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Build a fixed-structure weighted sum: c0*psi + c1*psi_star[0] + ...\n\nThe symbolic structure includes all terms up to len(coeffs)-1.\nInactive terms have coefficient=0 and vanish numerically.\n\nParameters\n----------\ncoeffs : list of UWexpression\n Coefficient expressions (length = order + 1).\npsi_fn : sympy expression\n Current-time field expression.\npsi_star_syms : list of sympy expressions\n History term symbolic expressions (psi_star[i].sym or psi_star[i]).\n\nReturns\n-------\nsympy expression\n The weighted sum.", + "harvested_comments": [], "status": "complete", "needs": [], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", + "name": "_setup_projections", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 580, + "file": "src/underworld3/systems/ddt.py", + "line": 1026, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "This will substitute specific information about this object", + "existing_docstring": "Initialize projection solvers for history updates.", "harvested_comments": [ - "feedback on this instance" + "## using this to store terms that can't be evaluated (e.g. derivatives)", + "The projection operator for mapping derivative values to the mesh - needs to be different for each variable type, unfortunately ...", + "Manifold meshes (dim < cdim): use the multi-component", + "projection to sidestep SNES_Vector's pre-manifold", + "mesh.dim/cdim entanglement. See sibling block in the" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "Eulerian", "is_public": false }, { - "name": "_replace_from_adapted_mesh", + "name": "_build_projection_source", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1448, - "signature": "(self, temp_var, adapted_mesh)", + "file": "src/underworld3/systems/ddt.py", + "line": 1952, + "signature": "(self, source_fn)", "parameters": [ { - "name": "temp_var", + "name": "source_fn", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Construct the row matrix used as the projection's ``uw_function``.\n\nApplies snapshot substitution (psi_star[0] \u2192 snap) when enabled.\nUsed by both ``psi_fn.setter`` and the ``initialise_history``\nfallback path so substitution semantics are consistent.", + "harvested_comments": [ + "Scalar / vector path: psi_star[0] is a scalar/vector field. If", + "snapshot is needed for these vtypes, extend here similarly." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "SemiLagrangian", + "is_public": false + }, + { + "name": "_activate_ale_for_traceback", + "kind": "method", + "file": "src/underworld3/systems/ddt.py", + "line": 2148, + "signature": "(self, dt_for_calc)", + "parameters": [ { - "name": "adapted_mesh", + "name": "dt_for_calc", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Replace internal storage after mesh adaptation.\n\nCalled by mesh.adapt() to update this variable's internal PETSc\nstructures after the mesh's discretization has changed. The data\nhas already been interpolated to temp_var; this method copies that\ndata into this variable's updated storage.\n\nParameters\n----------\ntemp_var : MeshVariable\n A temporary variable on the adapted mesh containing the\n interpolated data for this variable.\nadapted_mesh : Mesh\n The mesh object (same object, but with updated internal DM).\n\nNotes\n-----\nThis is an internal method called by mesh.adapt(). Users should\nnot need to call this directly.\n\nAfter this method returns, all user references to this variable\nremain valid and contain the interpolated data on the adapted mesh.", + "existing_docstring": "Populate ``self._v_mesh_var`` for the upcoming ALE trace-back.\n\nCalled from :meth:`update_pre_solve` when\n``self._pending_v_mesh_disp`` is set. Creates ``_v_mesh_var``\non first use (vector MeshVariable, degree 1, continuous \u2014\nsmooth enough for the trace-back's mid-point and is the\ncheapest discretisation that still resolves a per-node mesh\nvelocity), and writes ``data = \u0394x / dt`` so the SL trace-back\ncan evaluate ``V_fn \u2212 v_mesh`` at any point on the mesh by\nsympy subtraction or post-evaluation numpy subtraction.\n\nThe variable is REINIT-policy: its values are valid for the\nnext trace-back only, and the next adapt repopulates them\nfresh. The generic remesh pass skips it.\n\nReturns ``True`` if ALE is active (caller should subtract\nv_mesh at each V_fn evaluation), ``False`` otherwise.", "harvested_comments": [ - "Destroy old PETSc vectors", - "The mesh reference is still valid (same object, updated internals)", - "But we need to find/create our field on the new DM", - "Check if our field exists on the new DM", - "Need to add field to new DM (this happens if adapt() didn't pre-create variables)" + "Lazily create the v_mesh field. dim matches the mesh's", + "coordinate dimension (so it broadcasts cleanly against the", + "mesh-vector V_fn values).", + "disp has shape (n_nodes, cdim) and lives in mesh-coord", + "space; dt_for_calc is in the matching time scaling, so the" ], - "status": "complete", - "needs": [], - "parent_class": "_BaseMeshVariable", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", "is_public": false }, { - "name": "_create_array_view", + "name": "_consume_ale_pulse", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1593, + "file": "src/underworld3/systems/ddt.py", + "line": 2201, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Create array view of canonical data using appropriate conversion strategy.\n\nStrategy depends on variable complexity:\n- Scalars/Vectors: Simple reshape operations\n- 2D+ Tensors: Complex pack/unpack operations\n\nReturns\n-------\nArrayView\n Array-like object that delegates changes back to canonical data", + "existing_docstring": "Clear the one-step v_mesh pulse after the trace-back has used it.\n\nCalled at the end of :meth:`update_pre_solve` so subsequent\nnon-adapt steps see ``self._pending_v_mesh_disp is None`` and\nrun a plain trace-back. The MeshVariable storage is left in\nplace but its values become stale (REINIT policy on the var\nguarantees the generic remesh pass leaves it alone; the next\n:meth:`_activate_ale_for_traceback` rewrites .data fresh).", "harvested_comments": [], "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SemiLagrangian", "is_public": false }, { - "name": "_is_simple_variable", + "name": "_record_psi_star_from_field_data", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1611, + "file": "src/underworld3/systems/ddt.py", + "line": 2213, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Check if this is a simple scalar/vector variable (not a complex tensor)", + "existing_docstring": "Parallel-safe 'record current field into psi_star[0]'.\n\nThe default record step evaluates ``psi_fn`` at its own node\ncoordinates, which under MPI mis-locates on-vertex points at a\nprocess seam (first-pass ``get_closest_cells`` + FE extrapolation),\nseeding a spurious history value. When ``psi_fn`` is a single\nmesh-variable component living on this mesh with the same nodal\nlayout as ``psi_star[0]``, \"evaluate at own nodes\" is exactly that\nvariable's nodal data, so we copy it directly \u2014 no point location.\n\nReturns an array shaped like ``psi_star[0].array`` for that case, or\n``None`` (caller falls back to ``evaluate``) for non-scalar or\nexpression ``psi_fn`` (e.g. a flux with derivatives).", + "harvested_comments": [ + "sympy Matrix, row-major", + "scoped to scalar fields" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SemiLagrangian", + "is_public": false + }, + { + "name": "_exp_alpha", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 2806, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Convenience accessor for the ETD-2 ``\u03b1`` coefficient UWexpression.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SemiLagrangian", "is_public": false }, { - "name": "_create_simple_array_view", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1615, + "name": "_exp_phi", + "kind": "property", + "file": "src/underworld3/systems/ddt.py", + "line": 2811, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Array view for scalars/vectors using simple reshape operations", - "harvested_comments": [ - "Simple reshape: (-1, num_components) -> (N, a, b)", - "For simple variables, reshape to (N, a, b) format", - "Apply dimensionalization if needed", - "Check if variable has units and model has reference quantities", - "Variable has units - wrap with UnitAwareArray" - ], + "existing_docstring": "Convenience accessor for the ETD-2 ``\u03c6`` coefficient UWexpression.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SemiLagrangian", "is_public": false }, { - "name": "_create_tensor_array_view", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 1943, - "signature": "(self)", + "name": "_class_timer_decorator", + "kind": "function", + "file": "src/underworld3/timing.py", + "line": 334, + "signature": "(cls)", "parameters": [], "returns": null, - "existing_docstring": "Array view for complex tensors using pack/unpack operations", + "existing_docstring": "Decorator that adds timing to all methods in a class.\n\nWalks through class methods and wraps them with routine_timer_decorator.\n\nParameters\n----------\ncls : type\n Class to decorate\n\nReturns\n-------\ntype\n Same class with methods wrapped for timing\n\nExample\n-------\n>>> @uw.timing._class_timer_decorator\n>>> class MyAnalysis:\n>>> def compute(self):\n>>> # ... work ...\n>>>\n>>> uw.timing.start()\n>>> analysis = MyAnalysis()\n>>> analysis.compute() # Automatically timed\n>>> uw.timing.print_table()", "harvested_comments": [ - "Use complex pack/unpack for tensor layouts", - "Apply dimensionalization if needed", - "Check if variable has units and model has reference quantities", - "Variable has units - wrap with UnitAwareArray", - "Get variable units (needed for both branches)" + "... work ...", + "Automatically timed", + "Skip special methods", + "Skip already decorated", + "Create timed version" ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_get_ureg", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 43, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "Get the Pint unit registry.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_create_canonical_data_array", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2274, - "signature": "(self)", + "name": "_get_pint_helper", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 82, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "Create the single canonical data array with PETSc synchronization for MeshVariable.\nThis is the ONLY method that creates arrays with PETSc callbacks.\n\nHandles mesh-specific requirements like locking and ghost value synchronization.\n\nReturns\n-------\nNDArray_With_Callback\n Canonical array object with callback for automatic PETSc synchronization", - "harvested_comments": [ - "Ensure PETSc vector is available", - "Get direct access to PETSc vector in packed format", - "Create NDArray_With_Callback with proper shape and data", - "Single canonical callback for PETSc synchronization", - "Only act on data-changing operations" + "existing_docstring": "Get the Pint helper (lazy singleton).", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "partial", + "parent_class": null, + "is_public": false + }, + { + "name": "_get_default_backend", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 91, + "signature": "()", + "parameters": [], + "returns": null, + "existing_docstring": "DEPRECATED: Use _get_pint_helper() instead.", + "harvested_comments": [], + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_dimensionalise_stat", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2350, - "signature": "(self, value: Union[float, tuple]) -> Union[float, tuple]", + "name": "_extract_units_info", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 96, + "signature": "(obj)", "parameters": [ { - "name": "value", - "type_hint": "Union[float, tuple]", + "name": "obj", + "type_hint": null, "default": null, "description": "" } ], - "returns": "Union[float, tuple]", - "existing_docstring": "Helper to dimensionalise statistical values using uw.dimensionalise().\n\nTakes non-dimensional value(s) from PETSc and converts to dimensional\nform using the variable's units and model reference quantities.\n\nParameters\n----------\nvalue : float or tuple\n Non-dimensional value(s) from PETSc\n\nReturns\n-------\nfloat, tuple, or UWQuantity\n Dimensionalised value(s) if units are enabled, else unchanged", + "returns": null, + "existing_docstring": "Extract units information from various object types.\n\nArgs:\n obj: Object that might have units (variable, quantity, expression, etc.)\n\nReturns:\n tuple: (has_units, units, backend) or (False, None, None)", "harvested_comments": [ - "Check if units mode is enabled", - "Backward compatible - no units mode or variable has no units", - "Extract dimensionality from units", - "self.units is already a Pint Unit object with .dimensionality attribute", - "Pint Unit object - extract dimensionality directly" + "PRIORITY 0: Check for SymPy expressions first (including derivatives)", + "This must come before unit-aware object check because UWexpression wraps", + "SymPy derivatives and we need to detect the derivative, not the wrapper's units", + "Check if object has a SymPy expression inside (UWexpression, variables, etc.)", + "TRANSPARENT CONTAINER PRINCIPLE (2025-11-26): Check .sym property (UWexpression," ], "status": "complete", "needs": [], - "parent_class": "_BaseMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_scalar_stats", - "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2658, - "signature": "(self)", - "parameters": [], + "name": "_extract_units_from_sympy_expression", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 342, + "signature": "(expr)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Statistics for scalar variables (original implementation).", + "existing_docstring": "Extract units from SymPy expressions containing unit-aware variables.\n\nThis function analyzes mathematical expressions like 2*velocity to determine\ntheir units based on the unit-aware variables they contain.\n\nArgs:\n expr: SymPy expression\n\nReturns:\n tuple: (has_units, units, backend) or None if no units found", "harvested_comments": [ - "Now returns value directly, not tuple", - "Now returns value directly, not tuple" + "Import the function extraction utilities", + "Extract all UW objects (function symbols) from the expression", + "Get the default model to access registered variables", + "Map function symbols back to their variables", + "Skip coordinate symbols" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_analyze_expression_units", + "kind": "function", + "file": "src/underworld3/units.py", + "line": 410, + "signature": "(expr, unit_info_list)", + "parameters": [ + { + "name": "expr", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "unit_info_list", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Analyze a SymPy expression to determine its resultant units.\n\nThis implements basic dimensional analysis for mathematical operations:\n- Addition/subtraction: units must be the same, result has same units\n- Multiplication: units multiply\n- Division: units divide\n- Powers: units are raised to the power\n\nArgs:\n expr: SymPy expression\n unit_info_list: List of (units, backend) tuples from variables in expression\n\nReturns:\n Units for the resulting expression", + "harvested_comments": [ + "For now, implement simple heuristics", + "TODO: Full dimensional analysis implementation", + "If expression is multiplication, combine units using Pint", + "Check if it's a constant times a variable", + "Simple case: constant * variable" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "__set_name__", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 87, + "signature": "(self, owner, name)", + "parameters": [ + { + "name": "owner", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Called when descriptor is assigned to a class attribute.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SymbolicProperty", "is_public": false }, { - "name": "_vector_stats", + "name": "__get__", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2682, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Statistics for vector variables using magnitude.", - "harvested_comments": [ - "Create temporary scalar variable for magnitude", - "Compute magnitude: |v| = sqrt(v\u00b7v)", - "Get scalar stats on magnitude", - "Update with vector-specific info", - "Cleanup temporary variable" + "file": "src/underworld3/utilities/_api_tools.py", + "line": 92, + "signature": "(self, obj, objtype = None)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "objtype", + "type_hint": null, + "default": "None", + "description": "" + } ], + "returns": null, + "existing_docstring": "Get the stored value.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SymbolicProperty", "is_public": false }, { - "name": "_tensor_stats", + "name": "__set__", "kind": "method", - "file": "src/underworld3/discretisation/discretisation_mesh_variables.py", - "line": 2720, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/_api_tools.py", + "line": 98, + "signature": "(self, obj, value)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Statistics for tensor variables using Frobenius norm.", + "existing_docstring": "Set the value, with automatic unwrapping.", "harvested_comments": [ - "Create temporary scalar variable for Frobenius norm", - "Compute Frobenius norm: ||A||_F = sqrt(sum(A_ij^2))", - "Get scalar stats on Frobenius norm", - "Update with tensor-specific info", - "Cleanup temporary variable" + "Auto-unwrap objects with _sympify_() protocol", + "Auto-wrap in Matrix if requested", + "Only wrap if not already a Matrix", + "Mark solver as needing setup ONLY when property actually changes", + "Use 'is not' check first for speed" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "_BaseMeshVariable", + "parent_class": "SymbolicProperty", "is_public": false }, { - "name": "__new__", + "name": "__delete__", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 66, - "signature": "(cls, varname, mesh, *args, **kwargs)", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 124, + "signature": "(self, obj)", "parameters": [ { - "name": "varname", + "name": "obj", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Delete the stored value.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "SymbolicProperty", + "is_public": false + }, + { + "name": "__set_name__", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 220, + "signature": "(self, owner, name)", + "parameters": [ { - "name": "mesh", + "name": "owner", "type_hint": null, "default": null, "description": "" }, { - "name": "*args", + "name": "name", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Called when descriptor is assigned to a class attribute.", + "harvested_comments": [ + "Store the public name for introspection" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "ExpressionDescriptor", + "is_public": false + }, + { + "name": "__get__", + "kind": "method", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 227, + "signature": "(self, obj, objtype = None)", + "parameters": [ { - "name": "**kwargs", + "name": "obj", "type_hint": null, "default": null, "description": "" + }, + { + "name": "objtype", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Custom __new__ to ensure proper initialization and registration.", + "existing_docstring": "Get the persistent expression, creating it if needed.", "harvested_comments": [ - "Create the instance", - "Perform early registration to override any base variable registration", - "Register the wrapper immediately (this will overwrite any base variable registration)", - "Store reference for later use in __init__" + "Check if expression already exists", + "Create the expression ONCE", + "Import here to avoid circular imports", + "Get the name (may be dynamic based on object state)", + "Evaluate value_fn ONCE to get initial .sym" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "ExpressionDescriptor", "is_public": false }, { - "name": "_setup_registration", + "name": "__set__", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 197, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/_api_tools.py", + "line": 269, + "signature": "(self, obj, value)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Register with default model, replacing any existing registration from base variable.", + "existing_docstring": "Set the expression's symbolic content.\n\nFor parameters (read_only=False): Updates .sym or copies unit metadata\nFor templates (read_only=True): Raises error\n\nSpecial handling for UWQuantity:\n- If value is a pure UWQuantity (not UWexpression), copy unit metadata\n without changing the symbolic value to preserve lazy evaluation\n- Otherwise, update .sym as normal", "harvested_comments": [ - "All variables register with default model", - "Force registration of wrapper, even if base variable registered itself", - "Use either the early name (from __new__) or the processed name (from base variable)", - "This will overwrite any existing registration", - "Auto-derive scaling coefficient if model has reference quantities" + "Get or create the expression", + "Import here to avoid circular dependency", + "Special case: UWexpression assignment \u2014 store the expression itself", + "as a symbolic reference (not its inner ._sym value).", + "This preserves:" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "ExpressionDescriptor", "is_public": false }, { - "name": "_auto_derive_scaling_coefficient", + "name": "__get__", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 213, - "signature": "(self, model)", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 405, + "signature": "(self, obj, objtype = None)", "parameters": [ { - "name": "model", + "name": "obj", "type_hint": null, "default": null, "description": "" + }, + { + "name": "objtype", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Automatically derive scaling coefficient from model's reference quantities.", + "existing_docstring": "Get the persistent expression, re-evaluating if parameters have changed.\n\nWhen obj.is_setup is False, re-evaluates the lambda and updates the existing\nexpression's .sym in-place, preserving object identity while updating content.", "harvested_comments": [ - "Silently skip if module not available or model incomplete" + "Get existing expression (or create if first access)", + "Check if we need to refresh the symbolic content", + "This happens when parameters have changed (is_setup = False)", + "Re-evaluate the lambda to get updated symbolic content", + "Update the expression's .sym IN PLACE (preserves object identity)" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "Template", "is_public": false }, { - "name": "_setup_persistence_features", + "name": "_reset", "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 229, - "signature": "(self)", + "file": "src/underworld3/utilities/_api_tools.py", + "line": 474, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "Setup additional persistence capabilities for persistent variables.", - "harvested_comments": [ - "Store qualified name for later reference (for persistent variables)" - ], + "existing_docstring": "Reset the object counter", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "uw_object", "is_public": false }, { - "name": "_lvec", - "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 380, + "name": "_render_traceback_", + "kind": "method", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 104, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Local PETSc vector.", + "existing_docstring": "IPython/Jupyter custom traceback rendering.\n\nThis method is called by IPython when the exception is raised,\ndisplaying a clean message instead of a traceback.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "UW_Pause", "is_public": false }, { - "name": "_gvec", - "kind": "property", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 385, + "name": "__str__", + "kind": "method", + "file": "src/underworld3/utilities/_interrupt.py", + "line": 124, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Global PETSc vector.", + "existing_docstring": "String representation for non-Jupyter environments.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": "UW_Pause", + "is_public": false + }, + { + "name": "_file_lock", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 65, + "signature": "(path: Path)", + "parameters": [ + { + "name": "path", + "type_hint": "Path", + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Per-hash advisory lock so two processes don't race on the same entry.\n\nUses :func:`fcntl.flock` (POSIX). The lockfile is created if missing\nand kept around \u2014 wiping the cache directory removes it cleanly.\nBest-effort: if flock isn't supported (rare on POSIX), the body still\nruns; correctness then relies on the atomic ``os.replace`` in\n:func:`store_module`.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_manifest_from_constants", + "kind": "function", + "file": "src/underworld3/utilities/_jit_cache.py", + "line": 91, + "signature": "(constants_manifest)", + "parameters": [ + { + "name": "constants_manifest", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Canonicalise ``constants_manifest`` for JSON storage / comparison.\n\n``constants_manifest`` is a list of ``(index, UWexpression)`` tuples.\nWe persist only the stable ``expr.name`` \u2014 the current value is irrelevant\n(it's updated via ``PetscDSSetConstants`` at solve time).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, "is_public": false }, { - "name": "_set_vec", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 400, - "signature": "(self, available = True)", + "name": "_stable_sort_key", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 14, + "signature": "(obj)", "parameters": [ { - "name": "available", + "name": "obj", "type_hint": null, - "default": "True", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Initialize PETSc vector.", + "existing_docstring": "Return a process-stable key for ordering symbolic/JIT objects.\n\nPython hash randomisation means unordered containers such as ``set`` can\niterate differently on different MPI ranks. JIT source emission must not\ndepend on that ordering because every rank has to compile/load byte\nidentical C source.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 579, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Enhanced representation showing persistence and units info.", - "harvested_comments": [ - "Add persistence info", - "Add units info" + "name": "_stable_sorted", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 38, + "signature": "(iterable)", + "parameters": [ + { + "name": "iterable", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Sort *iterable* with a deterministic key suitable for JIT emission.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "__str__", - "kind": "method", - "file": "src/underworld3/discretisation/enhanced_variables.py", - "line": 593, - "signature": "(self)", + "name": "_petsc_build_env", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 44, + "signature": "()", "parameters": [], "returns": null, - "existing_docstring": "String representation.", + "existing_docstring": "Return a subprocess environment with PETSc's C/C++ compilers set.\n\nUnderworld's runtime JIT path shells out to a temporary ``setup.py``\nbuild. On some platforms the default compiler discovered by setuptools\nis not the same compiler family / wrapper PETSc was built with. Reuse\nPETSc's recorded ``CC`` and ``CXX`` from ``petscvariables`` so the JIT\nbuild follows the same toolchain as the main package build.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "EnhancedMeshVariable", + "parent_class": null, "is_public": false }, { - "name": "_is_enabled", - "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 48, - "signature": "(self) -> bool", + "name": "_abi_salt", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 164, + "signature": "()", "parameters": [], - "returns": "bool", - "existing_docstring": "Check if caching is enabled.", - "harvested_comments": [ - "Check mesh flag (default: True)" - ], - "status": "minimal", + "returns": null, + "existing_docstring": "Return a string that invalidates the cache when binary compatibility changes.\n\nKeeps the salt conservative (PETSc version + underworld3 version). The\n``./uw`` build driver is responsible for wiping the on-disk cache when\nthe compiler toolchain or Python ABI changes (see design doc).", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "DMInterpolationCache", + "parent_class": null, "is_public": false }, { - "name": "_hash_coords", + "name": "__post_init__", "kind": "method", - "file": "src/underworld3/function/dminterpolation_cache.py", - "line": 109, - "signature": "(self, coords: np.ndarray) -> int", - "parameters": [ - { - "name": "coords", - "type_hint": "np.ndarray", - "default": null, - "description": "" - } - ], - "returns": "int", - "existing_docstring": "Fast coordinate hashing for cache lookups.\n\nUses xxhash for speed (already used in old caching system).", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 221, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Coerce all slots to tuples for immutability and hashability.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "DMInterpolationCache", + "parent_class": "JITCallbackSet", "is_public": false }, { - "name": "_unwrap_atom", + "name": "_extract_constants", "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 76, - "signature": "(atom, mode = 'nondimensional')", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 323, + "signature": "(all_fns, mesh)", "parameters": [ { - "name": "atom", + "name": "all_fns", "type_hint": null, "default": null, "description": "" }, { - "name": "mode", + "name": "mesh", "type_hint": null, - "default": "'nondimensional'", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Extract the value from a single atom based on mode.\n\nArgs:\n atom: UWexpression, UWQuantity, UWCoordinate, or other symbol\n mode: 'nondimensional' - use .data for ND values (JIT/evaluate)\n 'dimensional' - use .value for display\n 'symbolic' - use .sym for symbolic substitution\n\nReturns:\n The unwrapped value (float, sympy.Number, or sympy expression)", + "existing_docstring": "Extract constant UWexpressions from a list of pre-unwrap functions.\n\nScans all expressions for UWexpression atoms where is_constant_expr()\nis True (no spatial/field dependencies). Assigns deterministic indices\nsorted by expression name for MPI consistency.\n\nParameters\n----------\nall_fns : tuple of sympy expressions\n The raw (pre-unwrap) function list.\nmesh : underworld3.discretisation.Mesh\n The mesh (currently unused, reserved for future mesh.t support).\n\nReturns\n-------\nlist of (int, UWexpression)\n Ordered mapping from constants[] index to UWexpression reference.\ndict\n Mapping from UWexpression to _JITConstant symbol for substitution.", "harvested_comments": [ - "UWCoordinate: always unwrap to BaseScalar (placeholder for evaluation)", - "UWexpression", - "User display: show dimensional value", - "JIT/evaluate: non-dimensionalize if scaling active", - "Recursively unwrap to get inner expression" + "Handle Matrix expressions", + "Sort by the user-given symbol name, not ``str(expr)`` \u2014 ``__str__`` on a", + "UWexpression returns the current *value*, which shuffles the index", + "assignment whenever a value changes. ``.name`` is stable.", + "Use ``expr.name`` (stable) instead of ``str(expr)`` (= current value)" ], "status": "complete", "needs": [], @@ -36955,11 +62624,11 @@ "is_public": false }, { - "name": "_unwrap_expression_once", + "name": "_is_truly_constant", "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 132, - "signature": "(expr, mode = 'nondimensional')", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 383, + "signature": "(expr, UWexpression)", "parameters": [ { "name": "expr", @@ -36968,3548 +62637,4162 @@ "description": "" }, { - "name": "mode", + "name": "UWexpression", "type_hint": null, - "default": "'nondimensional'", + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Single substitution pass over all UW atoms in an expression.\n\nUses free_symbols for reliable iteration (avoids issues with atoms()).\n\nArgs:\n expr: SymPy expression possibly containing UW atoms\n mode: See _unwrap_atom\n\nReturns:\n Expression with UW atoms substituted", + "existing_docstring": "Check if a UWexpression resolves to a pure constant (no spatial deps).\n\nUnlike is_constant_expr(), this handles nested UWexpressions correctly\nby fully unwrapping and checking if the result has any spatial/field\nsymbols (BaseScalar, UnderworldFunction, etc.).", "harvested_comments": [ - "Handle non-expression types directly", - "Unwrap the UWexpression itself first", - "Build substitution dict for all UW atoms" + "If it unwraps to a plain number, it's constant", + "Check remaining free symbols \u2014 any spatial/field dependency makes it non-constant", + "UnderworldFunction symbols have _ccodestr pointing to petsc arrays", + "Other UWexpressions that didn't fully unwrap \u2014 not constant" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false }, { - "name": "_unwrap_expressions", + "name": "_collect_constant_atoms", "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 325, - "signature": "(fn, keep_constants = True, return_self = True)", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 429, + "signature": "(expr, result_set, is_constant_expr, UWexpression)", "parameters": [ { - "name": "fn", + "name": "expr", "type_hint": null, "default": null, "description": "" }, { - "name": "keep_constants", + "name": "result_set", "type_hint": null, - "default": "True", + "default": null, "description": "" }, { - "name": "return_self", + "name": "is_constant_expr", "type_hint": null, - "default": "True", + "default": null, + "description": "" + }, + { + "name": "UWexpression", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Main unwrapping logic for JIT compilation.\n\nDEPRECATED: Use unwrap_expression(fn, mode='nondimensional') instead.\nThis function is preserved for backward compatibility.", + "existing_docstring": "Recursively collect constant UWexpression atoms from an expression.", "harvested_comments": [ - "Use unified implementation" + "Don't recurse into constant expressions", + "Non-constant UWexpression: check its inner sym for nested constants", + "Check all UWexpression atoms", + "Non-constant UWexpression: recurse into its sym" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], "parent_class": null, "is_public": false }, { - "name": "_unwrap_for_compilation", + "name": "_pack_constants", "kind": "function", - "file": "src/underworld3/function/expressions.py", - "line": 336, - "signature": "(fn, keep_constants = True, return_self = True)", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 454, + "signature": "(manifest)", "parameters": [ { - "name": "fn", + "name": "manifest", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Pack current values from a constants manifest into a flat array.\n\nParameters\n----------\nmanifest : list of (int, UWexpression)\n The constants manifest from _extract_constants().\n\nReturns\n-------\nlist of float\n Current nondimensional values in index order.", + "harvested_comments": [ + "Fallback: try .data property" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_createext", + "kind": "function", + "file": "src/underworld3/utilities/_jitextension.py", + "line": 1377, + "signature": "(name, mesh: underworld3.discretisation.Mesh, callbacks: JITCallbackSet, primary_field_list, constants_subs_map: Optional[dict] = None, verbose: Optional[bool] = False, debug: Optional[bool] = False, debug_name = None)", + "parameters": [ + { + "name": "name", "type_hint": null, "default": null, "description": "" }, { - "name": "keep_constants", + "name": "mesh", + "type_hint": "underworld3.discretisation.Mesh", + "default": null, + "description": "" + }, + { + "name": "callbacks", + "type_hint": "JITCallbackSet", + "default": null, + "description": "" + }, + { + "name": "primary_field_list", "type_hint": null, - "default": "True", + "default": null, "description": "" }, { - "name": "return_self", + "name": "constants_subs_map", + "type_hint": "Optional[dict]", + "default": "None", + "description": "" + }, + { + "name": "verbose", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "debug", + "type_hint": "Optional[bool]", + "default": "False", + "description": "" + }, + { + "name": "debug_name", "type_hint": null, - "default": "True", + "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "INTERNAL ONLY: Unwrap UW expressions to pure SymPy for JIT compilation.\n\nDEPRECATED: Use unwrap_expression(fn, mode='nondimensional') instead.", - "harvested_comments": [ - "Handle UWDerivativeExpression specially" - ], - "status": "minimal", + "existing_docstring": "Thin wrapper: generate source, compile, stash in ``_ext_dict[name]``.\n\nRetained for backwards compatibility with :func:`getext`. New code\nshould call :func:`generate_c_source` and :func:`compile_and_load`\ndirectly \u2014 splitting the two phases is what makes cache keys on the\ngenerated C source possible.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], "parent_class": null, "is_public": false }, { - "name": "_hashable_content", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 642, - "signature": "(self)", + "name": "_is_notebook", + "kind": "function", + "file": "src/underworld3/utilities/_nb_tools.py", + "line": 1, + "signature": "() -> bool", "parameters": [], - "returns": null, - "existing_docstring": "Include _uw_id in hash so symbols with same name but different IDs are distinct.\n\nThis follows the same pattern as sympy.Dummy which uses dummy_index.\nWhen _uw_id is None, symbols match by name alone (shared identity).\nWhen _uw_id is set, symbols with same name but different IDs are distinct.", - "harvested_comments": [], + "returns": "bool", + "existing_docstring": "Function to determine if the python environment is a Notebook or not.\n\nReturns 'True' if executing in a notebook, 'False' otherwise\n\nScript taken from https://stackoverflow.com/a/39662359/8106122", + "harvested_comments": [ + "Jupyter notebook or qtconsole", + "Terminal running IPython", + "Other type (?)", + "Probably standard Python interpreter" + ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__getnewargs_ex__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 655, - "signature": "(self)", + "name": "_is_interactive_vis", + "kind": "function", + "file": "src/underworld3/utilities/_nb_tools.py", + "line": 25, + "signature": "() -> bool", "parameters": [], - "returns": null, - "existing_docstring": "Support pickling by including _uw_id in reconstruction args.", - "harvested_comments": [ - "Return args and kwargs needed to reconstruct this symbol" - ], + "returns": "bool", + "existing_docstring": "Function to determine if interactive visualisation is available.\nReturns 'True' is possible, 'False' is otherwise", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "_latex", + "name": "_parse_cli_value", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 700, - "signature": "(self, printer)", + "file": "src/underworld3/utilities/_params.py", + "line": 190, + "signature": "(self, val_str: str, param: Param) -> Any", "parameters": [ { - "name": "printer", - "type_hint": null, + "name": "val_str", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "param", + "type_hint": "Param", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Custom LaTeX representation using _display_name.\n\nThis method is called by SymPy's LaTeX printer to get the\nrepresentation of this symbol. By overriding it, we can\ncontrol how the expression appears in LaTeX output without\nchanging its symbolic identity.\n\nParameters\n----------\nprinter : LatexPrinter\n The SymPy LaTeX printer instance (not used, but required by protocol).\n\nReturns\n-------\nstr\n The LaTeX representation of this expression.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "UWexpression", + "returns": "Any", + "existing_docstring": "Parse CLI string to appropriate type using Pint for quantities.\n\nFor QUANTITY type, uses Pint's parse_expression() which handles:\n \"500 m\", \"0.5km\", \"1e21 Pa*s\", \"500 meter\", etc.\n\nValidates dimensionality matches the units specified in Param definition.", + "harvested_comments": [ + "Use Pint's native string parsing - handles all unit formats", + "Check if result has units (Pint returns plain number if no units)", + "Plain number returned - no units provided", + "Note: We don't reject dimensionless quantities here because angles", + "(degrees, radians) are dimensionless but valid units. The dimension" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": "Params", "is_public": false }, { - "name": "_sympystr", + "name": "_validate_bounds", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 721, - "signature": "(self, printer)", + "file": "src/underworld3/utilities/_params.py", + "line": 247, + "signature": "(self, value: Any, param: Param, name: str) -> Any", "parameters": [ { - "name": "printer", - "type_hint": null, + "name": "value", + "type_hint": "Any", + "default": null, + "description": "" + }, + { + "name": "param", + "type_hint": "Param", + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Custom string representation using _display_name.\n\nThis method is called by SymPy's string printer to get the\nrepresentation of this symbol. Like _latex(), this allows\ncustomizing display without affecting identity.\n\nParameters\n----------\nprinter : StrPrinter\n The SymPy string printer instance (not used, but required by protocol).\n\nReturns\n-------\nstr\n The string representation of this expression.", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "UWexpression", + "returns": "Any", + "existing_docstring": "Validate value is within bounds after unit conversion.", + "harvested_comments": [ + "For quantities, convert to non-dimensional for comparison", + "Bounds should also be in same units or non-dimensional" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "Params", "is_public": false }, { - "name": "_compute_nondimensional_value", + "name": "_get_petsc_option", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 867, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/_params.py", + "line": 272, + "signature": "(self, opts, name: str, default)", + "parameters": [ + { + "name": "opts", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "default", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Internal: compute the non-dimensional value from the wrapped object.\n\nThis is the machinery that .data uses. Named explicitly to be self-documenting.", + "existing_docstring": "Get option value from PETSc with type-aware parsing.", "harvested_comments": [ - "TRANSPARENT CONTAINER: Derive from _sym (the wrapped object)", - "Delegate to wrapped object's .data", - "Fallback to dimensional value" + "Handle Param wrapper", + "Check if option exists before trying to get it", + "Parse and validate - let errors propagate", + "Return default value (convert to UWQuantity if needed)", + "Existing type handling for backward compatibility (match/case)" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "Params", "is_public": false }, { - "name": "_sympy_", + "name": "__getattr__", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1027, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/_params.py", + "line": 315, + "signature": "(self, name: str)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "SymPy protocol - return self (we ARE a Symbol).", + "existing_docstring": "Get parameter value.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "Params", "is_public": false }, { - "name": "_sympify_", + "name": "__setattr__", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1031, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/_params.py", + "line": 324, + "signature": "(self, name: str, value)", + "parameters": [ + { + "name": "name", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "SymPy sympify protocol - return self.", + "existing_docstring": "Set parameter value (override).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "Params", "is_public": false }, { - "name": "__bool__", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1039, + "file": "src/underworld3/utilities/_params.py", + "line": 339, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Always True for boolean contexts.", - "harvested_comments": [], - "status": "minimal", + "existing_docstring": "Show parameters with their sources.", + "harvested_comments": [ + "Format value (handle UWQuantity objects)", + "Add source indicator and units info", + "from -{name}{units_hint}\"", + "overridden (was {orig_val!r}){units_hint}\"", + "{units_hint}\" or \"\"" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": "Params", "is_public": false }, { - "name": "__hash__", + "name": "_repr_markdown_", "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1043, + "file": "src/underworld3/utilities/_params.py", + "line": 379, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Delegate to Symbol's hash.", - "harvested_comments": [], + "existing_docstring": "Rich display for Jupyter notebooks with type and units info.", + "harvested_comments": [ + "Extract type/units info from Param", + "Format value (handle UWQuantity objects)", + "Source with CLI hint" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": "Params", "is_public": false }, { - "name": "__eq__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1047, - "signature": "(self, other)", + "name": "_boundary_stratum_is", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 31, + "signature": "(dm, mesh, boundary)", "parameters": [ { - "name": "other", + "name": "dm", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Delegate to Symbol's equality (symbolic identity).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "__ne__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1051, - "signature": "(self, other)", - "parameters": [ + }, { - "name": "other", + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Delegate to Symbol's inequality.", + "existing_docstring": "Facet stratum IS for ``boundary`` via the CONSOLIDATED ``UW_Boundaries`` label\n(boundaries are distinguished by value on this one label \u2014 the per-boundary labels\nnamed like the boundary do not survive mesh adaptation; only ``UW_Boundaries`` is\nrebuilt). Returns None if this rank owns no part of the boundary. Raises a clear\nerror for an unknown boundary name.", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__mul__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1122, - "signature": "(self, other)", + "name": "_point_coord", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 47, + "signature": "(dm, dim, cvec, csec, v0, v1, q)", "parameters": [ { - "name": "other", + "name": "dm", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Multiplication - return UWexpression to preserve units.", - "harvested_comments": [ - "Handle matrix cases", - "Use applyfunc to multiply each element by self (as Symbol).", - "This preserves unit tracking: result is Matrix([x * self, ...])", - "where get_units() can find units for both self AND matrix elements.", - "DON'T use self._sym * other - that loses matrix element units!" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "__rmul__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1164, - "signature": "(self, other)", - "parameters": [ + }, { - "name": "other", + "name": "dim", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Right multiplication - handle UWQuantity, Matrix, and scalars.\n\nThis is called when `other * self` fails (other.__mul__ returns NotImplemented).\nIn particular, `MutableDenseMatrix * UWexpression` triggers this because\nSymPy matrices don't understand UWexpression.", - "harvested_comments": [ - "Handle UWQuantity - preserve units", - "Handle SymPy Matrix types - delegate to forward multiplication", - "Matrix * scalar works when scalar is on the left, so use self * other", - "which calls our __mul__ \u2192 Symbol.__mul__ \u2192 SymPy handles it correctly", - "scalar * Matrix is handled by SymPy - returns matrix with scaled elements" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "__truediv__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1194, - "signature": "(self, other)", - "parameters": [ + }, + { + "name": "cvec", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "csec", + "type_hint": null, + "default": null, + "description": "" + }, { - "name": "other", + "name": "v0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "v1", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "q", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Division - return UWexpression to preserve units.", - "harvested_comments": [ - "Handle UWQuantity - use full Pint arithmetic", - "Use FULL Pint quantity arithmetic", - "Handle UWexpression - preserve LAZY evaluation by returning SymPy quotient", - "Same design as __mul__: return raw SymPy quotient, derive units on demand", - "Scalar division - preserve self's units" - ], + "existing_docstring": "Coordinate of a DMPlex point (vertex \u2192 its coord; higher point \u2192 mean of its\nclosure vertices).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__rtruediv__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1247, - "signature": "(self, other)", + "name": "_boundary_field_nodes", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 57, + "signature": "(solver, boundary, field_id = 0)", "parameters": [ { - "name": "other", + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", "type_hint": null, "default": null, "description": "" + }, + { + "name": "field_id", + "type_hint": null, + "default": "0", + "description": "" } ], "returns": null, - "existing_docstring": "Right division - handle UWQuantity to preserve units.", - "harvested_comments": [ - "Scalar / expression - units become inverted", - "Only handle if units is a Pint unit object (not None or string)" - ], - "status": "minimal", + "existing_docstring": "DMPlex points carrying `field_id` DOFs on `boundary`, with their coordinates.\nParallel-safe: a rank owning no part of the boundary gets a NULL stratum IS\n(guarded); ghost/shared nodes are included and their partial per-rank reactions are\nsummed by coordinate in ``_desmear`` to form the complete reaction.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__add__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1268, - "signature": "(self, other)", + "name": "_node_normals", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 85, + "signature": "(solver, boundary, normal, nodes, dm, dim, cvec, csec, v0, v1)", "parameters": [ { - "name": "other", + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "normal", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "nodes", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cvec", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "csec", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "v0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "v1", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Addition - handle UWQuantity and UWexpression with unit conversion.", + "existing_docstring": "Per-node outward unit normal (only needed to project a vector reaction).\n``normal`` is None (geometric facet normal), a sympy 1\u00d7dim Matrix (analytic,\nlambdified), or a constant (dim,) vector.", "harvested_comments": [ - "Handle UWexpression + UWexpression", - "TRANSPARENT CONTAINER: If self.sym is UWQuantity, use proper Pint arithmetic", - "Both contain UWQuantity - use proper unit-aware addition", - "Pint will handle 10cm + 1m = 110cm automatically", - "Pass full UWQuantity - Transparent Container stores it" + "accumulate area-weighted facet normals to the closure nodes" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__radd__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1344, - "signature": "(self, other)", + "name": "_desmear", + "kind": "function", + "file": "src/underworld3/utilities/boundary_flux.py", + "line": 129, + "signature": "(solver, boundary, xs, R, mass, remove_mean, partial_reaction = True)", "parameters": [ { - "name": "other", + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "boundary", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "xs", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "R", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mass", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "remove_mean", "type_hint": null, "default": null, "description": "" + }, + { + "name": "partial_reaction", + "type_hint": null, + "default": "True", + "description": "" } ], "returns": null, - "existing_docstring": "Right addition - handle UWQuantity specially.", + "existing_docstring": "De-smear per-node reaction loads R (aligned with xs) into a pointwise flux via the\nboundary mass, assembled globally by a coordinate-keyed allgather so every rank forms\nthe identical system. Returns the flux at this rank's local nodes (xs order).\n\n``partial_reaction`` controls how a boundary node shared across a partition cut is\nreconciled across ranks: ``True`` (default) SUMS each rank's contribution \u2014 correct\nwhen R is the RAW per-rank volume residual (``boundary_flux``, DM overlap=0); ``False``\nOVERWRITES (all ranks already agree) \u2014 correct when R comes from an ASSEMBLED global\noperator, e.g. the rotated free-slip reaction ``Q(A\u00b7u \u2212 b)`` (``rotated_bc``).", "harvested_comments": [ - "Addition is commutative" + "no line-mass geometry yet in 3D \u2192 global-mean lumped fallback", + "Reconcile the nodal reaction across ranks by coordinate. partial_reaction=True: SUM", + "(raw per-rank residual, DM overlap=0, so a cut node holds only each rank's partial", + "\u2192 summing assembles the complete reaction, matching the rock-solid volume integral).", + "partial_reaction=False: OVERWRITE (already-assembled global reaction, all ranks agree" ], - "status": "minimal", + "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__sub__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1351, - "signature": "(self, other)", + "name": "_require_serial", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 107, + "signature": "(where)", "parameters": [ { - "name": "other", + "name": "where", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Subtraction - LAZY EVALUATION pattern.\n\nWhen subtracting UWexpressions, we preserve both symbols in the tree\nrather than doing eager arithmetic. This allows unwrap_for_evaluate()\nto substitute the correct nondimensional values later.\n\nThe key insight is that if one operand is a coordinate (no units) and\nthe other has units, we CANNOT do the subtraction eagerly because:\n- Coordinates are already in ND form (from mesh scaling)\n- Unit-bearing quantities need to be nondimensionalized by .data\n\nBy keeping both symbols, unwrap_for_evaluate can process each one\ncorrectly according to its type.", - "harvested_comments": [ - "Handle UWexpression - UWexpression: LAZY EVALUATION", - "Keep both symbols in the tree - don't do eager arithmetic", - "Delegate to SymPy Symbol subtraction - preserves both symbols", - "Handle UWexpression - UWQuantity: Wrap in UWexpression first (LAZY)", - "Wrap the UWQuantity in a UWexpression to preserve it as a symbol" - ], + "existing_docstring": "Custom-P transfers are serial-only (experimental). The reduced maps use\nrank-local DOF indices and the prolongations assemble as serial AIJ; at np>1\nthey would silently build wrong P / mis-numbered transfers. Parallel support\n(nested co-partitioned, rank-local P + MPIAIJ, global-section reduction) is a\ndesigned fast-follow \u2014 until then, fail loudly rather than wrong.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__rsub__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1391, - "signature": "(self, other)", + "name": "_sbr_apply", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 127, + "signature": "(dm, mark)", "parameters": [ { - "name": "other", + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "mark", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Right subtraction - handle UWQuantity specially.", - "harvested_comments": [ - "UWQuantity - UWexpression \u2192 UWexpression", - "Convert self to other's units (other is the \"base\" unit here)", - "Convert self's value to other's units for correct subtraction", - "other - self: result is in other's units", - "Use self.value (not self.sym) for arithmetic" - ], - "status": "minimal", + "existing_docstring": "Clone ``dm``, let ``mark(clone, adapt_label)`` flag cells, and SBR-refine.\nConforming (edge bisection + propagation, no hanging nodes), on-rank (no\nredistribution), no external mesh libraries. Cell geometry / marking is done\non the CLONE (the un-cloned mesh DM raises err73 from computeCellGeometryFVM).\n\nIMPORTANT: ``dm_plex_transform_type`` is a *global* PETSc option; leaving it as\n``refine_sbr`` breaks UW3's uniform ``dm.refine()`` (FMG hierarchy build) with\nPETSc error 73. It is set only for the duration of the call and restored.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__pow__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1423, - "signature": "(self, other)", + "name": "_reduce_to_global", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 188, + "signature": "(dm, full_coords)", "parameters": [ { - "name": "other", + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "full_coords", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Power - delegate to Symbol.", + "existing_docstring": "Reduce full local DOF coordinates to the solver's global (BC-eliminated,\nMG-ordered) layout by scattering each component through ``localToGlobal``\n(which drops constrained boundary DOFs and reorders to global indices).", "harvested_comments": [], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__rpow__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1427, - "signature": "(self, other)", + "name": "_field_subdm", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 221, + "signature": "(dm, field_id)", "parameters": [ { - "name": "other", + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_id", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Right power - delegate to Symbol.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "__neg__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1431, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Negation - delegate to Symbol.", + "existing_docstring": "Return the sub-DM for ``field_id`` (whole dm if single-field / None).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1439, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "User-friendly representation showing value with units.\n\nFor expressions with units, shows: value [units]\nFor expressions with symbolic content, shows: name = symbolic_expr\nFor named expressions with simple values, shows: name = value [units]\n\nNote: Uses _display_name (set via rename()) rather than _given_name\nso that renamed expressions show their custom names.", - "harvested_comments": [ - "Use _display_name for representation (respects rename())", - "Check if this is a \"named\" expression (user-defined name vs auto-generated)", - "Auto-generated names start with (", - "Format the value part", - "Has units - show value with units" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "name": "_reduced_map", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 234, + "signature": "(dm, field_id = None)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_id", + "type_hint": null, + "default": "None", + "description": "" + } ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "__str__", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1479, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "String representation showing value with units if available.", + "existing_docstring": "Reduced->full DOF map for a field on a BUILT DM (serial). Index ``k`` of\nthe returned array is the local DOF that maps to global/MG index ``k`` \u2014\ni.e. the BC-eliminated, MG-ordered layout the operator lives on.\n\nNOTE: serial-correct (uses local indices). Parallel correctness \u2014 mapping to\n*global* numbering across ranks \u2014 is a Phase-3 item (the supported parallel\npath keeps levels co-partitioned so this stays rank-local).", "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "_repr_latex_", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1488, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "LaTeX representation for Jupyter notebooks.\n\nJupyter prioritizes _repr_latex_ over __repr__, so we override\nSymPy's default to show units.", - "harvested_comments": [ - "Check if this is a \"named\" expression (user-defined name vs auto-generated)", - "Format value for LaTeX", - "Use scientific notation for very small/large numbers", - "Format units for LaTeX (Pint units have LaTeX-compatible format)", - "For named expressions, show name = value [units]" - ], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "_repr_html_", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1533, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "HTML representation for Jupyter notebooks (fallback if LaTeX not available).", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "_coarse_reduced_map", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 254, + "signature": "(solver, coarse_mesh, field_id = None)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coarse_mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_id", + "type_hint": null, + "default": "None", + "description": "" + } ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "_repr_png_", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1556, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Disable PNG rendering to ensure _repr_latex_ is used.\n\nSymPy's init_printing() may enable PNG rendering which bypasses\nour custom _repr_latex_. By returning None, we force Jupyter to\nfall back to text/latex format.", + "existing_docstring": "A COARSE level's BC-constrained reduced map, with NO throwaway solver.\n\nClone the coarse mesh DM and copy the (built) finest ``solver``'s fields + DS\nonto it. The DS carries UW's exact essential-BC definitions (the custom\nessential-field boundaries), and is a topology-independent discretisation spec,\nso ``createDS`` constrains the matching boundary DOFs on ANY coarse mesh that\ncarries the same boundary labels (nested or not). The resulting global section\ngives the same reduced map a full same-discretisation solver would \u2014 validated\nidentical to the old ``level_solver_factory`` path. Leak-free: DM ops only, no\nSNES / JIT.\n\nNOTE: serial, like ``_reduced_map`` (uses local indices); parallel correctness\nis a Phase-3 item.", "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "UWexpression", - "is_public": false - }, - { - "name": "_repr_svg_", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1566, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Disable SVG rendering to ensure _repr_latex_ is used.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "_repr_mimebundle_", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1570, - "signature": "(self, **kwargs)", + "name": "_reduced_transfer", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 275, + "signature": "(coarse_coords, fine_coords, r2f_c, r2f_f, ncomp, builder)", "parameters": [ { - "name": "**kwargs", + "name": "coarse_coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fine_coords", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "r2f_c", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "r2f_f", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "ncomp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "builder", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "MIME bundle for Jupyter display - highest priority representation.\n\nThis method has ABSOLUTE HIGHEST PRIORITY in Jupyter's display system.\nIt overrides ANY type-based formatters (including SymPy's init_printing()).\n\nWhy this is needed:\n- SymPy's init_printing() registers formatters for sympy.Basic types\n- UWexpression inherits from sympy.Symbol (a sympy.Basic subclass)\n- Without this, SymPy's formatter renders UWexpression as raw symbols\n- _repr_mimebundle_ cannot be overridden by type formatters\n\nReturns dict of MIME type \u2192 content for display.", + "existing_docstring": "Build one prolongation reduced(coarse) -> reduced(fine):\nnode-level scalar P -> interleave ``ncomp`` components -> drop BC rows/cols.", "harvested_comments": [ - "Get our custom LaTeX representation", - "Also provide plain text fallback" + "(n_f_nodes, n_c_nodes)", + "interleaved full vector", + "reduced -> reduced" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "_ipython_display_", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1596, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "IPython/Jupyter display hook - ABSOLUTE highest priority.\n\nThis method OVERRIDES MathematicalMixin._ipython_display_ to show\nour custom representation with units instead of raw SymPy symbols.\n\nWhy this override is needed:\n- MathematicalMixin._ipython_display_ calls display(Math(latex(sym)))\n- This shows only the symbol name without units\n- We want to show value + units for UWexpressions", - "harvested_comments": [ - "Use our custom LaTeX representation with units", - "IPython not available - silent fallback" + "name": "_level_dof_layout", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 293, + "signature": "(dm, field_id = None)", + "parameters": [ + { + "name": "dm", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_id", + "type_hint": null, + "default": "None", + "description": "" + } ], + "returns": null, + "existing_docstring": "Parallel DOF layout for one level: ``(l2g, rstart, rend, n_full)``.\n\n``l2g[i]`` is the GLOBAL reduced index of local DOF ``i`` \u2014 ghost-resolved to\nthe owner's global index, and ``-1`` for a BC-constrained DOF. Built by\nscattering each owned global index out to the local (incl. ghost) layout via\n``globalToLocal`` (constrained local DOFs have no global source, so they keep\nthe pre-set ``-1``). ``[rstart, rend)`` is this rank's owned global range.", + "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "_object_viewer", - "kind": "method", - "file": "src/underworld3/function/expressions.py", - "line": 1618, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Display expression details for solver .view() \"Where:\" section.\n\nShows the expression name, its symbolic value, and description.\nCalled by solver _object_viewer when expanding expression definitions.", - "harvested_comments": [ - "Build LaTeX representation: symbol = value", - "Get the symbolic value", - "Format the value", - "Strip $ signs if present (we'll add our own)", - "Display: name = value (description)" + "name": "_coarse_dof_layout", + "kind": "function", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 315, + "signature": "(solver, coarse_mesh, field_id = None)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "coarse_mesh", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "field_id", + "type_hint": null, + "default": "None", + "description": "" + } ], - "status": "partial", + "returns": null, + "existing_docstring": "Parallel coarse-level DOF layout, no throwaway solver \u2014 same copyDS trick\nas :func:`_coarse_reduced_map` but returning the parallel ``l2g`` layout.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "UWexpression", + "parent_class": null, "is_public": false }, { - "name": "_evaluate_gradient_interpolant", + "name": "_build_parallel_transfer", "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 124, - "signature": "(scalar_var, coords, component = None)", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 325, + "signature": "(cc, fc, lay_c, lay_f, ncomp, builder, comm)", "parameters": [ { - "name": "scalar_var", + "name": "cc", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fc", "type_hint": null, "default": null, "description": "" }, { - "name": "coords", + "name": "lay_c", "type_hint": null, "default": null, "description": "" }, { - "name": "component", + "name": "lay_f", "type_hint": null, - "default": "None", + "default": null, + "description": "" + }, + { + "name": "ncomp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "builder", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "comm", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Evaluate gradient via Clement interpolant (fast, O(h) accurate).\n\nUses PETSc's DMPlexComputeGradientClementInterpolant which averages\ncell-wise gradients at vertices. A scratch DM is used internally to\navoid polluting the mesh's DM.\n\nParameters\n----------\nscalar_var : MeshVariable\n Field to compute gradient of.\ncoords : array-like\n Coordinates at which to evaluate gradient.\ncomponent : int or None\n For multi-component fields, which component.\n\nReturns\n-------\nndarray\n Gradient values, shape (n_points, dim).", + "existing_docstring": "One reduced->reduced prolongation as an MPIAIJ matrix.\n\nNode-level barycentric/RBF weights are built rank-locally (coarse LOCAL coords\nincl. ghosts -> every owned fine node lands in a local coarse simplex). Each\nfine OWNED DOF becomes a global row; its coarse contributions map through the\ncoarse ``l2g`` to global columns (off-rank columns are fine for MPIAIJ).\nConstrained coarse DOFs (``l2g == -1``) drop out -> reduced->reduced.", "harvested_comments": [ - "Handle multi-component fields", - "Convert coords to numpy array if needed", - "Get vertex coordinates (P1 node locations)", - "Check if field is P1 scalar - can use data directly", - "Create scratch DM with P1 scalar field for Clement computation" + "(n_f_nodes, n_c_nodes), local", + "fine local node", + "set OWNED rows only", + "skip constrained coarse DOFs" + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false }, { - "name": "_evaluate_gradient_projection", + "name": "_assert_no_zero_columns_parallel", "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 271, - "signature": "(scalar_var, coords, component = None)", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 362, + "signature": "(P, comm)", "parameters": [ { - "name": "scalar_var", + "name": "P", "type_hint": null, "default": null, "description": "" }, { - "name": "coords", + "name": "comm", "type_hint": null, "default": null, "description": "" - }, - { - "name": "component", - "type_hint": null, - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": "Evaluate gradient via L2 projection (accurate, O(h\u00b2)).\n\nCreates a cached gradient MeshVariable and projector on the mesh.\nSubsequent calls reuse the cached objects and warm-start the solve\nfrom the previous solution.\n\nParameters\n----------\nscalar_var : MeshVariable\n Field to compute gradient of.\ncoords : array-like\n Coordinates at which to evaluate gradient.\ncomponent : int or None\n For multi-component fields, which component.\n\nReturns\n-------\nndarray\n Gradient values, shape (n_points, dim).", - "harvested_comments": [ - "Handle multi-component fields", - "Convert coords to numpy array if needed", - "Cache key based on variable and component", - "Initialize gradient cache on mesh if not present", - "Get or create cached projectors for each gradient component" + "existing_docstring": "Parallel zero-column guard: a coarse DOF with no fine image -> singular\nGalerkin coarse operator. Column sums via P^T\u00b71 (weights are positive, so a\nzero sum means an empty column).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false }, { - "name": "_compute_barycentric", + "name": "_configure_pcmg", "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 379, - "signature": "(point, vertices)", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 378, + "signature": "(pc, Ps)", "parameters": [ { - "name": "point", + "name": "pc", "type_hint": null, "default": null, "description": "" }, { - "name": "vertices", + "name": "Ps", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Compute barycentric coordinates of a point within a simplex.\n\nParameters\n----------\npoint : ndarray\n Query point, shape (dim,).\nvertices : ndarray\n Simplex vertices, shape (dim+1, dim).\n\nReturns\n-------\nndarray\n Barycentric coordinates, shape (dim+1,).", - "harvested_comments": [ - "Not a simplex, return uniform weights", - "Build matrix T where columns are (v_i - v_n) for i = 0..dim-1", - "shape (dim, dim)", - "Solve T @ lambda = (point - v_n)", - "Last barycentric coordinate" + "existing_docstring": "Reconfigure ``pc`` as a fresh PCMG (FMG F-cycle) driven by the supplied\nreduced->reduced prolongations ``Ps``, Galerkin RAP for coarse operators.\n\nWrites the MG bundle into the options DB under the PC's OWN options prefix\n(``pc.getOptionsPrefix()`` \u2014 e.g. ``Solver_N_`` for the scalar top-level PC,\n``Solver_N_fieldsplit_velocity_`` for the Stokes velocity sub-PC) and removes\nany gamg keys BEFORE ``setFromOptions`` \u2014 otherwise ``setFromOptions`` re-reads\na lingering ``pc_type=gamg`` and reverts ``setType(\"mg\")``. ``setMGInterpolation``\npersists through ``setFromOptions``; the first ``PCSetUp`` builds the coarse\noperators from our P (no ``MatProductReplaceMats`` shape bug, since the PCMG is\nfresh and P's size is fixed).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false }, { - "name": "_evaluate_field_at_vertices", + "name": "_install_transfers", "kind": "function", - "file": "src/underworld3/function/gradient_evaluation.py", - "line": 416, - "signature": "(var, component, mesh)", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 410, + "signature": "(solver, Ps, verbose = False)", "parameters": [ { - "name": "var", + "name": "solver", "type_hint": null, "default": null, "description": "" }, { - "name": "component", + "name": "Ps", "type_hint": null, "default": null, "description": "" }, { - "name": "mesh", + "name": "verbose", "type_hint": null, - "default": null, + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Evaluate a field component at P1 vertex locations.\n\nFor P1 scalar fields, returns the data directly.\nFor higher-degree or multi-component fields, samples at vertex coordinates.\n\nParameters\n----------\nvar : MeshVariable\n The mesh variable to sample.\ncomponent : int or None\n Which component to evaluate (for multi-component fields).\nmesh : Mesh\n The mesh.\n\nReturns\n-------\nndarray\n Field values at vertices, shape (n_vertices,).", - "harvested_comments": [ - "Get vertex count", - "P1 scalar - return data directly", - "P1 multi-component - extract the component directly from data", - "Data layout: [v0_c0, v0_c1, ..., v1_c0, v1_c1, ...]", - "Reshape to (n_vertices, num_components) and extract component" + "existing_docstring": "Configure the managed PCMG block to use the supplied prolongations.\n\nTwo paths, keyed by ``solver._pc_option_prefix``:\n\n* **scalar / single-field vector** (top-level PC, prefix ``\"\"``): build-time\n injection *before* the first ``PCSetUp`` \u2014 the first Galerkin assembly is\n built from our P directly.\n* **Stokes velocity block** (prefix ``\"fieldsplit_velocity_\"``): the velocity\n sub-PC is unreachable until the monolithic Jacobian is assembled\n (``PCFieldSplit`` forms ``A_vv`` via ``MatCreateSubMatrix``; ``snes.setUp``\n builds structure only -> err73). So force a Jacobian assembly, reach the\n velocity sub-PC, ``reset`` it and rebuild a fresh PCMG from our P, then\n re-attach the coupled Stokes nullspace.\n\nEither way ``KSPSetDMActive(OPERATOR, False)`` stops PETSc re-deriving the\ninterpolation from the DM hierarchy.", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false }, { - "name": "_expr_hash", + "name": "_install_velocity_block_transfers", "kind": "function", - "file": "src/underworld3/function/pure_sympy_evaluator.py", - "line": 126, - "signature": "(expr)", + "file": "src/underworld3/utilities/custom_mg.py", + "line": 448, + "signature": "(solver, Ps, verbose = False)", "parameters": [ { - "name": "expr", + "name": "solver", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Generate a hash for a sympy expression for caching.\n\nParameters\n----------\nexpr : sympy expression\n Expression to hash\n\nReturns\n-------\nstr\n Hash string", - "harvested_comments": [ - "Use sympy's srepr for consistent string representation" - ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": false - }, - { - "name": "_from_pint", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 100, - "signature": "(cls, pint_qty, model_registry = None)", - "parameters": [ + }, { - "name": "pint_qty", + "name": "Ps", "type_hint": null, "default": null, "description": "" }, { - "name": "model_registry", + "name": "verbose", "type_hint": null, - "default": "None", + "default": "False", "description": "" } ], "returns": null, - "existing_docstring": "Create UWQuantity from a Pint Quantity object.\n\nThis is used by Model.to_model_units() and other internal methods\nthat work with Pint quantities directly.\n\nParameters\n----------\npint_qty : pint.Quantity\n A Pint Quantity object\nmodel_registry : pint.UnitRegistry, optional\n Model-specific registry (for model units)\n\nReturns\n-------\nUWQuantity\n New quantity with the Pint quantity's value and units", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "UWQuantity", - "is_public": false - }, - { - "name": "_compute_nd_value", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 161, - "signature": "(self) -> Union[float, np.ndarray]", - "parameters": [], - "returns": "Union[float, np.ndarray]", - "existing_docstring": "Compute the non-dimensional value using model scaling.", + "existing_docstring": "Stokes velocity-block install (mechanism A: reset + fresh PCMG).\n\nPreconditions: ``solver._build`` + ``setFromOptions`` + ``_attach_stokes_nullspace``\nhave run (the call site in ``SNES_Stokes_SaddlePt.solve`` guarantees this), so\nthe SNES / DM exist but the Jacobian VALUES are not yet assembled.", "harvested_comments": [ - "If no units, value is already \"non-dimensional\"", - "Try to get scaling from model", - "Get the scale factor for our dimensionality", - "Extract scalar from scale (may be Pint Quantity)", - "Convert to base units first, then scale" + "1. force monolithic Jacobian assembly so the fieldsplit can form A_vv", + "fallback: throwaway max_it=0 solve assembles + splits the operator", + "Wire the freshly-assembled Jacobian into the KSP/outer-PC. SNESSolve does", + "this lazily, but we reach the fieldsplit BEFORE the solve \u2014 without it the", + "outer PC can carry an unassembled operator and PCSetUp fails with" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWQuantity", + "parent_class": null, "is_public": false }, { - "name": "__add__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 280, - "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", + "name": "_get_extension_library_paths", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 42, + "signature": "(extension_path: str) -> List[str]", "parameters": [ { - "name": "other", - "type_hint": "Union['UWQuantity', float, int]", + "name": "extension_path", + "type_hint": "str", "default": null, "description": "" } ], - "returns": "'UWQuantity'", - "existing_docstring": "Addition via Pint.", + "returns": "List[str]", + "existing_docstring": "Get the libraries an extension is linked against.\n\nUses otool on macOS, ldd on Linux.", "harvested_comments": [ - "One or both dimensionless", - "Handle UWexpression", - "Delegate to UWexpression's __radd__", - "Handle SymPy expressions - LAZY EVALUATION approach", - "Wrap self in UWexpression first, then add symbolically" + "Skip first line (filename)" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": null, "is_public": false }, { - "name": "__radd__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 324, - "signature": "(self, other)", + "name": "_resolve_rpath", + "kind": "function", + "file": "src/underworld3/utilities/diagnostics.py", + "line": 88, + "signature": "(lib_ref: str, extension_path: str) -> Optional[str]", "parameters": [ { - "name": "other", - "type_hint": null, + "name": "lib_ref", + "type_hint": "str", "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Right addition.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "UWQuantity", - "is_public": false - }, - { - "name": "__sub__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 328, - "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", - "parameters": [ + }, { - "name": "other", - "type_hint": "Union['UWQuantity', float, int]", + "name": "extension_path", + "type_hint": "str", "default": null, "description": "" } ], - "returns": "'UWQuantity'", - "existing_docstring": "Subtraction via Pint.", + "returns": "Optional[str]", + "existing_docstring": "Resolve @rpath references to actual library paths.\n\nOn macOS, @rpath is resolved using DYLD_LIBRARY_PATH and embedded rpaths.", "harvested_comments": [ - "Handle UWexpression: UWQuantity - UWexpression \u2192 UWexpression", - "For subtraction, units must be compatible - convert other to self's units", - "Convert other's value to self's units", - "Units incompatible - use raw value (will be wrong but won't crash)", - "Result in self's units" + "Check DYLD_LIBRARY_PATH (macOS) or LD_LIBRARY_PATH (Linux)", + "Check common conda/pixi paths" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": null, "is_public": false }, { - "name": "__rsub__", + "name": "_create_nondimensional_expression", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 389, - "signature": "(self, other)", - "parameters": [ - { - "name": "other", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/utilities/dimensionality_mixin.py", + "line": 120, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Right subtraction: other - self.", + "existing_docstring": "Create non-dimensional symbolic expression", "harvested_comments": [ - "Handle UWexpression: UWexpression - UWQuantity is handled by UWexpression.__sub__", - "This handles: sympy.Basic - UWQuantity", - "LAZY EVALUATION approach (like __add__):", - "Wrap self in UWexpression first, preserving the full UWQuantity.", - "This ensures evaluate() knows the value has units and won't re-scale it." + "Create starred symbol for non-dimensional version", + "For functions like T(x,y,z)", + "Use same arguments", + "For symbols like eta", + "For expressions, divide by scale" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "DimensionalityMixin", "is_public": false }, { - "name": "__mul__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 421, - "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", + "name": "_parse_parameter_section", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 139, + "signature": "(content: str) -> Dict[str, Tuple[str, str]]", "parameters": [ { - "name": "other", - "type_hint": "Union['UWQuantity', float, int]", + "name": "content", + "type_hint": "str", "default": null, "description": "" } ], - "returns": "'UWQuantity'", - "existing_docstring": "Multiplication via Pint.", + "returns": "Dict[str, Tuple[str, str]]", + "existing_docstring": "Parse a Parameters or Returns section.\n\nFormat:\n param_name : type\n Description that may span\n multiple lines.", "harvested_comments": [ - "Handle UnitAwareArray - Pint doesn't properly combine units with UnitAwareArray", - "We need to manually combine units and multiply values", - "Both have units - combine them via Pint", - "Multiply numeric values (extract numpy from UnitAwareArray)", - "Return UnitAwareArray with combined units" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "Pattern: name : type\\n description", + "Check if this is a new parameter definition", + "Save previous parameter", + "Continuation of description", + "Save last parameter" ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__rmul__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 544, - "signature": "(self, other)", + "name": "_rst_math_to_latex", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 316, + "signature": "(text: str) -> str", "parameters": [ { - "name": "other", - "type_hint": null, + "name": "text", + "type_hint": "str", "default": null, "description": "" } ], - "returns": null, - "existing_docstring": "Right multiplication.", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "returns": "str", + "existing_docstring": "Convert RST math notation to LaTeX for Jupyter display.\n\nParameters\n----------\ntext : str\n Text with RST math notation.\n\nReturns\n-------\nstr\n Text with LaTeX math notation.", + "harvested_comments": [ + ":math:`x` -> $x$", + ".. math:: block -> $$...$$ block", + "Remove common leading whitespace", + "Find minimum indentation (excluding empty lines)" ], - "parent_class": "UWQuantity", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "__truediv__", - "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 548, - "signature": "(self, other: Union['UWQuantity', float, int]) -> 'UWQuantity'", + "name": "_latex_to_rst_math", + "kind": "function", + "file": "src/underworld3/utilities/docstring_utils.py", + "line": 358, + "signature": "(text: str) -> str", "parameters": [ { - "name": "other", - "type_hint": "Union['UWQuantity', float, int]", + "name": "text", + "type_hint": "str", "default": null, "description": "" } ], - "returns": "'UWQuantity'", - "existing_docstring": "Division via Pint.", + "returns": "str", + "existing_docstring": "Convert LaTeX notation to RST for pdoc3/Sphinx.\n\nParameters\n----------\ntext : str\n Text with LaTeX math notation.\n\nReturns\n-------\nstr\n Text with RST math notation.", "harvested_comments": [ - "Check if other is a UWexpression - handle specially to preserve units", - "Both have units - compute combined units (self / other)", - "Only self has units", - "Only other has units - result has 1/other_units", - "Neither has units - just delegate to SymPy" + "$$...$$ -> .. math:: block (do this first to avoid conflicts)", + "$x$ -> :math:`x` (but not $$)" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_validate_sym", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 42, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Validate that sym property is available and valid.", + "harvested_comments": [ + "Try to access sym property - this should not trigger __getattr__ recursion", + "since __getattr__ now guards against accessing private methods", + "Genuinely missing sym property", + "Recursion loop in sym property access", + "For other exceptions (like RuntimeError), preserve the original exception type" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__rtruediv__", + "name": "_sympy_", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 624, - "signature": "(self, other)", - "parameters": [ - { - "name": "other", - "type_hint": null, - "default": null, - "description": "" - } - ], + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 67, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Right division: other / self.\n\nLAZY EVALUATION approach (same as __rsub__ and __add__):\nWrap self in UWexpression first, preserving the full UWQuantity.\nThis ensures evaluate() knows the value has units and won't re-scale it.\n\nRED FLAG: Never embed bare numbers (self._value) in symbolic expressions!\nBare numbers get re-dimensionalized during evaluation, causing unit bugs.", - "harvested_comments": [ - "Handle SymPy types - use LAZY EVALUATION", - "LAZY EVALUATION approach:", - "Wrap self in UWexpression first, preserving the full UWQuantity.", - "This ensures evaluate() knows the value has units and won't re-scale it.", - "Store the full UWQuantity - Transparent Container" - ], - "status": "partial", + "existing_docstring": "SymPy protocol: Tell SymPy to use the symbolic form.\n\nNote: Uses _sympy_() (not _sympify_()) for SymPy 1.14+ strict mode compatibility.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__pow__", + "name": "__getitem__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 661, - "signature": "(self, exponent: Union[float, int]) -> 'UWQuantity'", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 74, + "signature": "(self, index)", "parameters": [ { - "name": "exponent", - "type_hint": "Union[float, int]", + "name": "index", + "type_hint": null, "default": null, "description": "" } ], - "returns": "'UWQuantity'", - "existing_docstring": "Exponentiation via Pint.", - "harvested_comments": [], + "returns": null, + "existing_docstring": "Component access with proper bounds checking.", + "harvested_comments": [ + "Check for scalar variables (no indexing allowed)", + "For matrices, validate index bounds", + "For matrices, check against the total number of components", + "Row vectors have shape (1, n) - use cols, column vectors have shape (n, 1) - use rows", + "Row vector - check against columns" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__neg__", + "name": "__iter__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 669, - "signature": "(self) -> 'UWQuantity'", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 111, + "signature": "(self)", "parameters": [], - "returns": "'UWQuantity'", - "existing_docstring": "Negation.", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": "Allow iteration for matrix-type UWexpressions.\n\nSymPy's simplification code tries to iterate over Symbol subclasses,\ntreating them as atomic elements. For Matrix types, we delegate to\nSymPy's iteration. For scalar Symbols, we raise TypeError to indicate\nthey are atomic and should not be iterated.\n\nThis prevents: TypeError: 'UWexpression' object (scalar) is not subscriptable\nwhen SymPy's cancel() \u2192 factor_terms() tries to factorize expressions.", + "harvested_comments": [ + "Only Matrix-like objects should be iterable", + "Check if it's actually a SymPy Matrix type", + "Matrix/vector - delegate to SymPy's iteration", + "Scalar Symbol - raise TypeError to indicate it's atomic", + "This tells SymPy not to iterate over it" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_sympy_", + "name": "__len__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 723, + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 134, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "SymPy protocol - controls how SymPy converts this object.\n\nFor quantities WITH units: raise SympifyError to force SymPy to\nreturn NotImplemented, which triggers our __rmul__/__radd__ etc.\n\nFor quantities WITHOUT units: return the numeric value.", + "existing_docstring": "Length method for SymPy sequence compatibility.", "harvested_comments": [ - "If we have units, don't let SymPy consume us silently", - "This forces SymPy to return NotImplemented so our __rmul__ gets called", - "No units - safe to return numeric value" + "For matrices, return the length (number of elements)", + "For scalars or objects without len, raise TypeError" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__float__", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 747, + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 145, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Convert to float.", - "harvested_comments": [], + "existing_docstring": "String representation returns the symbolic form.", + "harvested_comments": [ + "Fallback for broken variables" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__str__", + "name": "_repr_latex_", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 759, - "signature": "(self) -> str", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 154, + "signature": "(self)", "parameters": [], - "returns": "str", - "existing_docstring": "String representation matching UWexpression style: value [units].", + "returns": null, + "existing_docstring": "Jupyter notebook LaTeX representation.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__repr__", + "name": "_ipython_display_", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 765, - "signature": "(self) -> str", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 164, + "signature": "(self)", "parameters": [], - "returns": "str", - "existing_docstring": "User-friendly representation matching UWexpression style: value [units].", - "harvested_comments": [], + "returns": null, + "existing_docstring": "IPython/Jupyter display hook.", + "harvested_comments": [ + "IPython not available - silent fallback", + "Broken sym - show type name" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "__format__", + "name": "__add__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 771, - "signature": "(self, format_spec: str) -> str", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 239, + "signature": "(self, other)", "parameters": [ { - "name": "format_spec", - "type_hint": "str", + "name": "other", + "type_hint": null, "default": null, "description": "" } ], - "returns": "str", - "existing_docstring": "Formatted representation matching UWexpression style.", - "harvested_comments": [], + "returns": null, + "existing_docstring": "Addition with scalar broadcasting and error handling.", + "harvested_comments": [ + "Convert MathematicalMixin objects to their symbolic form", + "IMPORTANT: If other is already a sympy.Basic (e.g., UWexpression), use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_repr_latex_", + "name": "__radd__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 786, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "LaTeX representation for Jupyter notebooks.", - "harvested_comments": [ - "Format value for LaTeX", - "Use scientific notation for very small/large numbers", - "Format units for LaTeX" + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 287, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Right addition with scalar broadcasting.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_repr_mimebundle_", + "name": "__sub__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 807, - "signature": "(self, **kwargs)", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 291, + "signature": "(self, other)", "parameters": [ { - "name": "**kwargs", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "MIME bundle for Jupyter display - highest priority representation.\n\nThis method has ABSOLUTE HIGHEST PRIORITY in Jupyter's display system.", - "harvested_comments": [], + "existing_docstring": "Subtraction with scalar broadcasting and error handling.", + "harvested_comments": [ + "Convert MathematicalMixin objects to their symbolic form", + "IMPORTANT: If other is already a sympy.Basic (e.g., UWexpression), use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_ipython_display_", + "name": "__rsub__", "kind": "method", - "file": "src/underworld3/function/quantities.py", - "line": 818, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 335, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "IPython/Jupyter display hook - ABSOLUTE highest priority.\n\nShows the quantity with units in LaTeX format.", + "existing_docstring": "Right subtraction with scalar broadcasting.", "harvested_comments": [ - "IPython not available - silent fallback" + "Convert MathematicalMixin objects to their symbolic form", + "IMPORTANT: If other is already a sympy.Basic (e.g., UWexpression), use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "UWQuantity", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_extract_value", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 15, - "signature": "(value, target_units = None)", + "name": "__mul__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 379, + "signature": "(self, other)", "parameters": [ { - "name": "value", + "name": "other", "type_hint": null, "default": null, "description": "" - }, - { - "name": "target_units", - "type_hint": null, - "default": "None", - "description": "" } ], "returns": null, - "existing_docstring": "Universal helper to extract numeric value from quantities or pass through numbers.\n\nThis is a lightweight, zero-side-effect function that makes APIs accept both\nplain numbers and unit-aware quantities transparently.\n\nParameters\n----------\nvalue : float, int, Quantity, UWQuantity, tuple, list, or None\n Value to extract. Can be:\n - Plain number \u2192 returned as-is\n - Pint Quantity \u2192 magnitude extracted\n - UWQuantity \u2192 value extracted\n - tuple/list \u2192 recursively processed\n - None \u2192 returned as-is\ntarget_units : str, optional\n If provided, converts quantity to these units before extracting value.\n Ignored if value has no units.\n\nReturns\n-------\nfloat, int, tuple, list, or None\n Plain numeric value(s), ready to use in numeric APIs\n\nExamples\n--------\n>>> # Plain numbers pass through unchanged\n>>> _extract_value(5.0)\n5.0\n\n>>> # Quantities have magnitude extracted\n>>> _extract_value(5.0 * uw.units.km)\n5000.0 # in meters\n\n>>> # With target units\n>>> _extract_value(5.0 * uw.units.km, 'cm')\n500000.0\n\n>>> # Tuples/lists processed recursively\n>>> _extract_value((0.0, 5.0 * uw.units.km))\n(0.0, 5000.0)\n\n>>> # UWQuantity support\n>>> _extract_value(uw.quantity(5.0, \"km\"))\n5000.0", + "existing_docstring": "Multiplication.", "harvested_comments": [ - "Plain numbers pass through unchanged", - "Quantities have magnitude extracted", - "With target units", - "Tuples/lists processed recursively", - "UWQuantity support" + "Extract symbolic form from other", + "IMPORTANT: If other is already a sympy.Basic (e.g., UWexpression which IS a Symbol),", + "use it directly. Don't extract .sym which might return UWQuantity that SymPy", + "matrices can't handle.", + "UWexpression, plain Symbol, etc. - use directly" ], - "status": "complete", - "needs": [], - "parent_class": null, + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_convert_coords_to_si", - "kind": "function", - "file": "src/underworld3/function/unit_conversion.py", - "line": 1001, - "signature": "(coords)", + "name": "__rmul__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 424, + "signature": "(self, other)", "parameters": [ { - "name": "coords", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert coordinate input to numpy array in model coordinates.\n\nThis function handles unit-aware coordinates by converting them to model units,\nnot SI units. This ensures coordinates work correctly with meshes that use\nreference quantities for scaling.\n\nAccepts:\n- numpy arrays (assumed to be in model coordinates if no units)\n- lists/tuples of coordinates (each coordinate can be UWQuantity, Pint Quantity, or float/int)\n- lists/tuples of tuples (for multiple points)\n\nReturns numpy array of shape (n_points, n_dims) with dtype=np.double in model coordinates.", + "existing_docstring": "Right multiplication.", "harvested_comments": [ - "Get the model for unit conversion", - "Helper function to convert a single coordinate value", - "Unit-aware coordinate - convert to model units", - "Extract the magnitude", - "Conversion returned plain number - use it" + "Extract symbolic form from other", + "IMPORTANT: If other is already a sympy.Basic, use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" ], - "status": "partial", + "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_convert_coords_to_tree_units", + "name": "__truediv__", "kind": "method", - "file": "src/underworld3/kdtree.py", - "line": 68, - "signature": "(self, coords)", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 459, + "signature": "(self, other)", "parameters": [ { - "name": "coords", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert query coordinates to match the KD-tree's coordinate system.\n\nParameters\n----------\ncoords : array-like\n Query coordinates (may or may not have units)\n\nReturns\n-------\nnp.ndarray\n Coordinates converted to tree's coordinate system (raw numpy array)", + "existing_docstring": "Division.", "harvested_comments": [ - "If tree has no units, just extract raw array", - "Tree has units - check query coordinates", - "Same units - just extract raw array", - "Different units - convert to tree's coordinate system", - "Use UnitAwareArray's to method if available" + "Extract symbolic form from other", + "IMPORTANT: If other is already a sympy.Basic, use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" ], - "status": "complete", - "needs": [], - "parent_class": "KDTree", + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_notify_callbacks", + "name": "__rtruediv__", "kind": "method", - "file": "src/underworld3/materials.py", - "line": 304, - "signature": "(self, event_type: str, *args)", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 492, + "signature": "(self, other)", "parameters": [ { - "name": "event_type", - "type_hint": "str", - "default": null, - "description": "" - }, - { - "name": "*args", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Notify all callbacks of a material change", - "harvested_comments": [], + "existing_docstring": "Right division.", + "harvested_comments": [ + "Extract symbolic form from other", + "IMPORTANT: If other is already a sympy.Basic, use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "MaterialRegistry", + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_rank2_to_unscaled_matrix", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 91, - "signature": "(v_ij, dim, covariant = True)", + "name": "__pow__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 528, + "signature": "(self, other)", "parameters": [ { - "name": "v_ij", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", + "name": "other", "type_hint": null, "default": null, "description": "" - }, - { - "name": "covariant", - "type_hint": null, - "default": "True", - "description": "" } ], "returns": null, - "existing_docstring": "Convert rank 2 tensor (v_ij) to voigt (vector) form (V_I)", - "harvested_comments": [], + "existing_docstring": "Power.", + "harvested_comments": [ + "Convert MathematicalMixin objects to their symbolic form", + "IMPORTANT: If other is already a sympy.Basic, use it directly", + "Unit-bearing scalar (UWQuantity / Pint) -> non-dimensional value, so", + "it composes with the variable's non-dimensional .sym. Compatible", + "units reduce via the model scaling; sympy cannot operate on the Pint" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_rank4_to_unscaled_matrix", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 112, - "signature": "(c_ijkl, dim)", + "name": "__rpow__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 563, + "signature": "(self, other)", "parameters": [ { - "name": "c_ijkl", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert rank 4 tensor (c_ijkl) to matrix form (C_IJ)", - "harvested_comments": [], + "existing_docstring": "Right power with error handling.", + "harvested_comments": [ + "Convert MathematicalMixin objects to their symbolic form", + "This ensures both operands are SymPy objects, avoiding type mismatches", + "Matrix/vector exponents often not mathematically meaningful" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_unscaled_matrix_to_rank2", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 134, - "signature": "(V_I, dim)", - "parameters": [ - { - "name": "V_I", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", - "type_hint": null, - "default": null, - "description": "" - } + "name": "__neg__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 583, + "signature": "(self)", + "parameters": [], + "returns": null, + "existing_docstring": "Negation.", + "harvested_comments": [ + "TRANSPARENT CONTAINER PRINCIPLE: Return raw SymPy, units derived on demand" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], + "parent_class": "MathematicalMixin", + "is_public": false + }, + { + "name": "__pos__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 592, + "signature": "(self)", + "parameters": [], "returns": null, - "existing_docstring": "Convert to rank 2 tensor (v_ij) from voigt (vector) form (V_I)", + "existing_docstring": "Positive (identity operation).", "harvested_comments": [ - "convert Voight form V_I to v_ij (matrix)" + "Some SymPy objects don't support unary +, just return the symbol" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_unscaled_matrix_to_rank4", - "kind": "function", - "file": "src/underworld3/maths/tensors.py", - "line": 153, - "signature": "(C_IJ, dim)", + "name": "__getattr__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 720, + "signature": "(self, name)", "parameters": [ { - "name": "C_IJ", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "dim", + "name": "name", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert to rank 4 tensor (c_ijkl) from matrix form (C_IJ)", + "existing_docstring": "Enhanced method delegation with signature handling.", "harvested_comments": [ - "C_IJ -> C_ijkl -> C_jilk -> C_ij_lk -> C_jikl (Symmetry)" + "Prevent recursion. \"sym\" is guarded because _validate_sym below calls", + "self.sym \u2014 if the sym @property getter raises AttributeError (e.g.", + "accessing internal state not yet set during __init__), Python falls", + "back to __getattr__, which re-enters _validate_sym and loops. Names", + "starting with \"_\" are guarded so internal attribute probing (e.g." ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "MathematicalMixin", "is_public": false }, { - "name": "_require_pyvista", - "kind": "function", - "file": "src/underworld3/meshing/faults.py", - "line": 56, - "signature": "()", + "name": "_sympy_", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 820, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Check pyvista availability with helpful error message.", + "existing_docstring": "SymPy protocol: Return the underlying SymPy Matrix for arithmetic operations.\n\nNote: Uses _sympy_() (not _sympify_()) for SymPy 1.14+ strict mode compatibility.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UnitAwareDerivativeMatrix", "is_public": false }, { "name": "__getitem__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 451, - "signature": "(self, name: str) -> FaultSurface", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 964, + "signature": "(self, index)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "index", + "type_hint": null, "default": null, "description": "" } ], - "returns": "FaultSurface", - "existing_docstring": "Get a fault by name.", - "harvested_comments": [], - "status": "minimal", + "returns": null, + "existing_docstring": "Index into matrix and wrap result with units.\n\nPasses the derivative units explicitly to ensure indexed elements\nhave correct units (e.g., kelvin / kilometer for temperature derivative).", + "harvested_comments": [ + "Pass derivative units explicitly to avoid having to infer from SymPy structure", + "Convert units to string since with_units() expects a string" + ], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "FaultCollection", + "parent_class": "UnitAwareDerivativeMatrix", "is_public": false }, { - "name": "__iter__", + "name": "_repr_latex_", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 455, + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 978, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Iterate over fault names.", + "existing_docstring": "Jupyter LaTeX representation.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "FaultCollection", + "parent_class": "UnitAwareDerivativeMatrix", "is_public": false }, { "name": "__len__", "kind": "method", - "file": "src/underworld3/meshing/faults.py", - "line": 459, + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 991, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Number of faults in collection.", + "existing_docstring": "Return length of matrix for SymPy compatibility.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "FaultCollection", + "parent_class": "UnitAwareDerivativeMatrix", "is_public": false }, { - "name": "_require_pyvista", - "kind": "function", - "file": "src/underworld3/meshing/surfaces.py", - "line": 58, - "signature": "()", - "parameters": [], + "name": "__getattr__", + "kind": "method", + "file": "src/underworld3/utilities/mathematical_mixin.py", + "line": 1021, + "signature": "(self, name)", + "parameters": [ + { + "name": "name", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Check pyvista availability with helpful error message.", + "existing_docstring": "Delegate other attributes to the underlying matrix.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], + "parent_class": "UnitAwareDerivativeMatrix", + "is_public": false + }, + { + "name": "_rss_mb", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 81, + "signature": "() -> float", + "parameters": [], + "returns": "float", + "existing_docstring": "Current resident-set size in MiB.\n\nPrefers ``psutil.Process().memory_info().rss`` (true current RSS, drops\nwhen memory is freed). Falls back to ``/proc/self/statm`` on Linux when\npsutil isn't available, then to ``resource.ru_maxrss`` (peak/high-water\nRSS \u2014 does not decrease) as a last resort. The fallback is good enough\nfor spotting growth, less good for spotting recovery.", + "harvested_comments": [ + "statm: size resident shared text lib data dt (in pages)", + "Last resort: peak RSS \u2014 does NOT decrease, so growth shows but", + "recovery does not. ru_maxrss is KiB on Linux, bytes on macOS." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], "parent_class": null, "is_public": false }, { - "name": "_create_proxy", + "name": "_python_class_counts", + "kind": "function", + "file": "src/underworld3/utilities/memprobe.py", + "line": 129, + "signature": "(prefix: str = 'underworld3') -> Counter[str]", + "parameters": [ + { + "name": "prefix", + "type_hint": "str", + "default": "'underworld3'", + "description": "" + } + ], + "returns": "Counter[str]", + "existing_docstring": "Count live Python objects whose class lives under ``prefix``.\n\nWalks ``gc.get_objects()`` once. Slow at large counts (~10\u2013100 ms for\ntypical UW runs). Only called when ``full=True`` is requested.", + "harvested_comments": [ + "Some objects expose a non-string descriptor as ``__module__``;", + "skip those rather than crashing the walk." + ], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_get_state", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 254, - "signature": "(self) -> None", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 47, + "signature": "(self)", "parameters": [], - "returns": "None", - "existing_docstring": "Create the proxy MeshVariable for .sym access.", + "returns": null, + "existing_docstring": "Get or create thread-local state.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceVariable", + "parent_class": "DelayedCallbackManager", "is_public": false }, { - "name": "_interpolate_to_proxy", + "name": "__new__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 263, - "signature": "(self) -> None", - "parameters": [], - "returns": "None", - "existing_docstring": "Interpolate surface vertex data to mesh nodes.\n\nEach rank populates its LOCAL mesh nodes only using inverse distance\nweighting from surface vertices.", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 150, + "signature": "(cls, input_array = None, owner = None, callback = None, disable_inplace_operators = False)", + "parameters": [ + { + "name": "input_array", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "owner", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "callback", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "disable_inplace_operators", + "type_hint": null, + "default": "False", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Create new NDArray_With_Callback instance.\n\nParameters\n----------\ninput_array : array-like, optional\n Input data to create array from (defaults to empty array if None)\nowner : object, optional\n The object that owns this array (stored as weak reference)\ncallback : callable, optional\n Initial callback function to register\ndisable_inplace_operators : bool, optional\n If True, in-place operators (``+=``, ``-=``, ``*=``, ``/=``, etc.)\n will raise RuntimeError for parallel safety.\n Default is False for backward compatibility.", "harvested_comments": [ - "Get local mesh node coordinates", - "Get surface vertex data", - "For 2D surfaces, use only x,y components for KDTree", - "Build KDTree for surface vertices", - "Find nearest surface vertex for each mesh node" + "Create the ndarray instance", + "Initialize callback system", + "Register initial callback if provided" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", "NEEDS_RETURNS" ], - "parent_class": "SurfaceVariable", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_mark_all_proxies_stale", + "name": "__array_finalize__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 496, - "signature": "(self) -> None", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 185, + "signature": "(self, obj)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Called whenever the system allocates a new array from this template.", + "harvested_comments": [ + "Copy callback information from parent array" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": false + }, + { + "name": "_mask", + "kind": "property", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 203, + "signature": "(self)", "parameters": [], - "returns": "None", - "existing_docstring": "Mark all SurfaceVariable proxies as stale.", + "returns": null, + "existing_docstring": "For numpy.ma compatibility - we have no mask.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Surface", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_ensure_discretized", + "name": "_mask", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 568, - "signature": "(self) -> None", - "parameters": [], - "returns": "None", - "existing_docstring": "Ensure discretization is computed (lazy evaluation).", - "harvested_comments": [], + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 208, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "For numpy.ma compatibility - ignore mask setting.", + "harvested_comments": [ + "We don't support masking, so ignore attempts to set a mask" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Surface", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_discretize_3d", + "name": "_update_from", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 601, - "signature": "(self, offset: float = 0.01) -> None", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 246, + "signature": "(self, obj)", "parameters": [ { - "name": "offset", - "type_hint": "float", - "default": "0.01", + "name": "obj", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Discretize 3D control points into triangulated mesh using pyvista delaunay_2d.", + "returns": null, + "existing_docstring": "For numpy.ma compatibility - update from another array.", "harvested_comments": [ - "Check for degenerate cases (all points nearly collinear)", - "Create PolyData from points and triangulate", - "Compute normals (both point and cell)" + "This is used by masked array operations to update data" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Surface", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_discretize_2d", + "name": "__array_wrap__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 633, - "signature": "(self, n_segments: int = None) -> None", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 254, + "signature": "(self, result, context = None, return_scalar = False)", "parameters": [ { - "name": "n_segments", - "type_hint": "int", + "name": "result", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "context", + "type_hint": null, "default": "None", "description": "" + }, + { + "name": "return_scalar", + "type_hint": null, + "default": "False", + "description": "" } ], - "returns": "None", - "existing_docstring": "Create 2D surface as ordered line segments using scipy spline fitting.\n\nFor 2D, a \"surface\" is a 1D curve (polyline) embedded in 2D space.", + "returns": null, + "existing_docstring": "Called after numpy operations to wrap results back to our type.\n\nParameters updated for NumPy 2.0 compatibility:\n- context: Information about the ufunc that produced the result (unused)\n- return_scalar: If True, return a scalar instead of 0-d array", "harvested_comments": [ - "Get 2D coordinates", - "For just 2 points, connect them directly", - "Use scipy to fit a parametric spline through points", - "This naturally orders points along the curve", - "s=0 means interpolate exactly through points" + "Scalar result, return as numpy scalar", + "For in-place operations that return the same array, return self", + "Use numpy's view to avoid recursion", + "NB: require a NON-None shared base for the base-equality branch.", + "A fresh ufunc result (e.g. scalar * arr) has base=None; if self" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": false + }, + { + "name": "_trigger_callback", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 482, + "signature": "(self, operation: str, indices = None, old_value = None, new_value = None, data_has_changed = True)", + "parameters": [ + { + "name": "operation", + "type_hint": "str", + "default": null, + "description": "" + }, + { + "name": "indices", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "old_value", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "new_value", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "data_has_changed", + "type_hint": null, + "default": "True", + "description": "" + } + ], + "returns": null, + "existing_docstring": "Internal method to trigger all registered callbacks.\n\nParameters\n----------\noperation : str\n Name of the operation that triggered the callback\nindices : tuple or slice, optional\n Indices that were modified\nold_value : array-like, optional\n Previous value(s) at the modified location\nnew_value : array-like, optional\n New value(s) at the modified location\ndata_has_changed : bool, optional\n Whether this operation may have changed the array data (default True)", + "harvested_comments": [ + "Check if we're in a delay callback context", + "Add callbacks to the delayed execution queue", + "Execute callbacks immediately", + "Copy in case callbacks modify the list" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": false + }, + { + "name": "__setitem__", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 527, + "signature": "(self, key, value)", + "parameters": [ + { + "name": "key", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Override setitem to trigger callbacks on assignment.", + "harvested_comments": [ + "Handle UnitAwareArray values by extracting magnitude", + "This allows: T.array[...] = uw.function.evaluate(...) where evaluate returns UnitAwareArray", + "Without this, numpy raises \"only length-1 arrays can be converted to Python scalars\"", + "UnitAwareArray or similar - extract the raw numeric data", + "Perform the actual assignment" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Surface", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_compute_distance_field", + "name": "__iadd__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 765, - "signature": "(self) -> None", - "parameters": [], - "returns": "None", - "existing_docstring": "Compute signed distance field from mesh nodes to surface.\n\nThe signed distance is positive on one side of the surface and\nnegative on the other. Helper functions like influence_function()\nuse sympy.Abs() when unsigned distance is needed.\n\nFor 3D surfaces: Uses pyvista's compute_implicit_distance.\nFor 2D surfaces: Uses geometry_tools signed_distance_pointcloud_polyline_2d.", - "harvested_comments": [ - "Use varsymbol for clean LaTeX display: d_{F} instead of {surf_fault_distance}", - "Always wrap symbol in braces for proper LaTeX grouping (e.g., d_{F_1} not d_F_1)", - "Get mesh coordinates", - "2D: Use geometry_tools for signed distance to polyline", - "Get 2D coordinates" + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 551, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "partial", + "returns": null, + "existing_docstring": "In-place addition with callback.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Surface", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_on_mesh_adapted", + "name": "__isub__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 991, - "signature": "(self, adapted_mesh: 'Mesh') -> None", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 568, + "signature": "(self, other)", "parameters": [ { - "name": "adapted_mesh", - "type_hint": "'Mesh'", + "name": "other", + "type_hint": null, "default": null, "description": "" } ], - "returns": "None", - "existing_docstring": "Called by mesh.adapt() to update after mesh adaptation.\n\nMarks the distance field as stale so it will be recomputed on next access.\nThe surface geometry (control points, pyvista mesh) is unchanged -\nonly the cached distance values need updating.\n\nThe distance MeshVariable itself is reinitialized by mesh.adapt() along\nwith all other MeshVariables - we just need to mark the data as stale.\n\nArgs:\n adapted_mesh: The mesh (same object, updated internals)", - "harvested_comments": [ - "Mark distance as stale - will be recomputed on next access", - "The MeshVariable stays in mesh._vars and gets reinitialized by adapt()", - "just like any other variable (same pattern as swarm proxy variables)", - "Mark all variable proxies as stale (they project to mesh nodes)" - ], - "status": "partial", + "returns": null, + "existing_docstring": "In-place subtraction with callback.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Surface", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "__getitem__", + "name": "__imul__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1334, - "signature": "(self, name: str) -> Surface", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 585, + "signature": "(self, other)", "parameters": [ { - "name": "name", - "type_hint": "str", + "name": "other", + "type_hint": null, "default": null, "description": "" } ], - "returns": "Surface", - "existing_docstring": "Get a surface by name.", + "returns": null, + "existing_docstring": "In-place multiplication with callback.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "__iter__", + "name": "__itruediv__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1338, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 602, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Iterate over surface names.", + "existing_docstring": "In-place true division with callback.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "__len__", + "name": "__ifloordiv__", "kind": "method", - "file": "src/underworld3/meshing/surfaces.py", - "line": 1342, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 619, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Number of surfaces in collection.", + "existing_docstring": "In-place floor division with callback.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SurfaceCollection", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_register_mesh", + "name": "__imod__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 155, - "signature": "(self, mesh)", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 636, + "signature": "(self, other)", "parameters": [ { - "name": "mesh", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Internal method to register a mesh with this model.\nCalled automatically from Mesh.__init__\n\nParameters:\n-----------\nmesh : uw.discretisation.Mesh\n Mesh instance to register", - "harvested_comments": [ - "Set as primary mesh if no primary mesh exists" - ], - "status": "partial", + "existing_docstring": "In-place modulo with callback.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_register_swarm", + "name": "__ipow__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 200, - "signature": "(self, swarm)", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 653, + "signature": "(self, other)", "parameters": [ { - "name": "swarm", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Internal method to register a swarm with this model.\nCalled automatically from Swarm.__init__\n\nParameters:\n-----------\nswarm : uw.swarm.Swarm\n Swarm instance to register", + "existing_docstring": "In-place power with callback.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_register_variable", + "name": "__iand__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 213, - "signature": "(self, name, variable)", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 670, + "signature": "(self, other)", "parameters": [ { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "variable", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Internal method to register a variable with this model.\nCalled automatically from MeshVariable/SwarmVariable.__init__\n\nHandles namespace collision by creating fully qualified names for variables\non different meshes/swarms but with the same symbolic name.\n\nParameters:\n-----------\nname : str\n Variable name (may not be unique across meshes/swarms)\nvariable : MeshVariable or SwarmVariable\n Variable instance to register", - "harvested_comments": [ - "For SwarmVariables, ensure we keep strong reference to swarm to prevent garbage collection", - "This will raise if swarm already garbage collected", - "Ensure swarm is registered with strong reference", - "Check for namespace collision", - "If same variable object, nothing to do" - ], - "status": "partial", + "existing_docstring": "In-place bitwise and with callback.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_register_solver", + "name": "__ior__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 311, - "signature": "(self, name, solver)", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 687, + "signature": "(self, other)", "parameters": [ { - "name": "name", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "solver", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Internal method to register a solver with this model.\nCalled automatically from solver.__init__\n\nParameters:\n-----------\nname : str\n Solver name/identifier\nsolver : Solver instance\n Solver to register", + "existing_docstring": "In-place bitwise or with callback.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_update_mesh_for_swarm", + "name": "__ixor__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 350, - "signature": "(self, swarm, new_mesh)", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 704, + "signature": "(self, other)", "parameters": [ { - "name": "swarm", + "name": "other", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "In-place bitwise xor with callback.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": "NDArray_With_Callback", + "is_public": false + }, + { + "name": "__ilshift__", + "kind": "method", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 721, + "signature": "(self, other)", + "parameters": [ { - "name": "new_mesh", + "name": "other", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Internal method to coordinate mesh handover for a specific swarm.\n\nThis method handles the model-level coordination when a swarm\nis assigned to a new mesh via swarm.mesh = new_mesh.\n\nParameters\n----------\nswarm : uw.swarm.Swarm\n Swarm being reassigned to new mesh\nnew_mesh : uw.discretisation.Mesh\n New mesh to assign\n\nNotes\n-----\nThis is called from swarm.mesh setter and handles:\n- Unregistering swarm from old mesh\n- Updating model's mesh reference\n- Registering swarm with new mesh", - "harvested_comments": [ - "Unregister swarm from old mesh", - "Mesh may not support swarm registration or swarm not registered", - "Update model's mesh reference", - "Register swarm with new mesh" - ], - "status": "partial", + "existing_docstring": "In-place left shift with callback.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_lock_units", + "name": "__irshift__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 924, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 738, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Lock the model units to prevent changes after mesh creation.\n\nThis is called automatically when the first mesh is registered.\nOnce locked, calling set_reference_quantities() will raise an error.", + "existing_docstring": "In-place right shift with callback.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_check_units_locked", + "name": "__reduce__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 933, + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 909, "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Check if model units are locked.\n\nRaises:\n-------\nRuntimeError\n If units are locked and user tries to change reference quantities", - "harvested_comments": [], - "status": "partial", + "existing_docstring": "Support for pickling.", + "harvested_comments": [ + "Get the parent's reduce result", + "Add our custom attributes to the state" + ], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_simple_dimensional_analysis", + "name": "__setstate__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 972, - "signature": "(self) -> dict", - "parameters": [], - "returns": "dict", - "existing_docstring": "Pure mathematical dimensional analysis using linear algebra.\n\nUses physics (dimensional structure) not linguistics (names) to derive\nfundamental scales. Works with any user terminology.", - "harvested_comments": [], - "status": "partial", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 924, + "signature": "(self, state)", + "parameters": [ + { + "name": "state", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Support for unpickling.", + "harvested_comments": [ + "Split our custom attributes from the parent's state", + "Call parent's setstate" + ], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_comprehensive_dimensional_analysis", + "name": "__repr__", "kind": "method", - "file": "src/underworld3/model.py", - "line": 981, - "signature": "(self) -> dict", + "file": "src/underworld3/utilities/nd_array_callback.py", + "line": 935, + "signature": "(self)", "parameters": [], - "returns": "dict", - "existing_docstring": "Comprehensive dimensional analysis using linear algebra and Pint.\n\nAnalyzes dimensional coverage, provides intelligent error handling,\nand uses human-friendly formatting. Zero dependency on naming conventions.", + "returns": null, + "existing_docstring": "String representation showing callback information.", "harvested_comments": [ - "Use the SHARED underworld3 unit registry to avoid registry mismatch errors", - "Build dimensional matrix from pure physics", - "Analyze system properties", - "Handle different system types", - "Always show warnings (parallel-safe)" + "Insert callback info before the closing parenthesis" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": "NDArray_With_Callback", "is_public": false }, { - "name": "_solve_available_dimensions", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1037, - "signature": "(self, matrix, names, fundamental_dims, ureg)", + "name": "_derive_scale_for_variable", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 42, + "signature": "(var, fundamental_scales, reference_quantities)", "parameters": [ { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "names", + "name": "var", "type_hint": null, "default": null, "description": "" }, { - "name": "fundamental_dims", + "name": "fundamental_scales", "type_hint": null, "default": null, "description": "" }, { - "name": "ureg", + "name": "reference_quantities", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Solve for fundamental scales from the dimensions available in reference quantities.\n\nDerives scales for whatever dimensions are covered by the user's reference\nquantities (e.g., L, M, T from viscosity, velocity, length). Dimensions not\ncovered are left undefined and will only cause errors if actually requested.\nThis follows Pint's approach: work with what's available.", + "existing_docstring": "Derive the scaling coefficient for a variable based on its dimensionality.\n\nArgs:\n var: Variable with dimensionality property\n fundamental_scales: Dictionary of fundamental scales (length, time, mass, etc.)\n reference_quantities: Dictionary of reference quantities\n\nReturns:\n Scaling coefficient or None if unable to derive", "harvested_comments": [ - "Identify which dimensions are covered (have non-zero entries in matrix)", - "Informational message about missing dimensions (not an error!)", - "Extract sub-matrix for covered dimensions only", - "Get magnitudes in SI base units", - "Can we solve the sub-system?" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "First check if any reference quantity matches the variable's dimensionality exactly", + "Direct match found - convert to base units first", + "Fallback if conversion fails", + "Try to construct from fundamental scales using Pint dimensionality objects", + "For common variable types, use heuristics" ], - "parent_class": "Model", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "_solve_complete_system", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1124, - "signature": "(self, matrix, magnitudes, names, fundamental_dims, ureg)", + "name": "_construct_from_fundamental_scales", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 85, + "signature": "(var_dimensionality, fundamental_scales)", "parameters": [ { - "name": "matrix", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "magnitudes", - "type_hint": null, - "default": null, - "description": "" - }, - { - "name": "names", + "name": "var_dimensionality", "type_hint": null, "default": null, "description": "" }, { - "name": "fundamental_dims", + "name": "fundamental_scales", "type_hint": null, "default": null, "description": "" - }, + } + ], + "returns": null, + "existing_docstring": "Construct a scaling coefficient from fundamental scales using dimensional powers.\n\nUses LINEAR ALGEBRA: scale = L^a * T^b * M^c * \u03b8^d where [a,b,c,d] are the\ndimensional exponents from the Pint dimensionality object.\n\nArgs:\n var_dimensionality: Pint dimensionality object, dict, or string representation\n fundamental_scales: Dict with 'length', 'time', 'mass', 'temperature' Pint Quantity scales\n\nReturns:\n Scaling coefficient (float) or None if scales are incomplete", + "harvested_comments": [ + "Map Pint dimension names to our fundamental_scales keys", + "Convert dimensionality to a dict of {dimension: power}", + "Parse string like \"[length] / [time]\" or \"[length] ** 2 / [time]\"", + "Pint UnitsContainer or similar dict-like object", + "Try direct dict conversion as fallback" + ], + "status": "complete", + "needs": [], + "parent_class": null, + "is_public": false + }, + { + "name": "_parse_dimensionality_string", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 151, + "signature": "(dim_str)", + "parameters": [ { - "name": "ureg", + "name": "dim_str", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Solve complete dimensional system using linear algebra.\n\nGiven n reference quantities with known dimensionalities, solves for the\nfundamental scales [L, T, M, \u03b8] that are consistent with all constraints.\n\nThe system is: matrix @ log10(scales) = log10(magnitudes)\nwhere matrix[i,j] is the power of fundamental dimension j in quantity i.", + "existing_docstring": "Parse a dimensionality string like \"[length] / [time]\" into a dict.\n\nExamples:\n \"[length]\" -> {\"[length]\": 1}\n \"[length] / [time]\" -> {\"[length]\": 1, \"[time]\": -1}\n \"[length] ** 2 / [time]\" -> {\"[length]\": 2, \"[time]\": -1}\n \"[mass] / [length] ** 3\" -> {\"[mass]\": 1, \"[length]\": -3}\n\nReturns:\n dict mapping dimension names to powers, or None if parsing fails", "harvested_comments": [ - "Solve the linear system in log space", - "Create Pint quantities for the fundamental scales - direct, no rounding", - "Verification (only shown if verbose=True)" + "Handle dimensionless", + "Split by \"/\" to get numerator and denominator", + "Process numerator (positive powers)", + "Process denominator (negative powers)" ], "status": "partial", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_create_pint_registry", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1178, - "signature": "(self)", - "parameters": [], + "name": "_parse_dim_part", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 191, + "signature": "(part, result_dict, positive = True)", + "parameters": [ + { + "name": "part", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "result_dict", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "positive", + "type_hint": null, + "default": "True", + "description": "" + } + ], "returns": null, - "existing_docstring": "Create Pint registry with model-specific _constants.\n\nUses Pint's _constants pattern like _100km for model units.", + "existing_docstring": "Parse a part of dimensionality string (numerator or denominator).", "harvested_comments": [ - "Create model-specific registry", - "Store constant definitions for inspection", - "Define multiple aliases for fundamental dimensions", - "ASCII aliases: Easy to type", - "Unicode aliases: Beautiful for display" + "Find all dimensions with optional exponents", + "Matches: [length], [length] ** 2, [time] ** 3, etc.", + "Accumulate powers for same dimension" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_substitute_display_aliases", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1285, - "signature": "(self, units_str: str) -> str", + "name": "_check_derivability_from_dimensions", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 269, + "signature": "(target_dim, ref_dimensions, ureg)", "parameters": [ { - "name": "units_str", - "type_hint": "str", + "name": "target_dim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "ref_dimensions", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "ureg", + "type_hint": null, "default": null, "description": "" } ], - "returns": "str", - "existing_docstring": "Replace raw constant names with elegant display aliases in unit strings.\n\nExamples:\n\"_6p31e41kg\" \u2192 \"\u2133\"\n\"_1000km\" \u2192 \"\u2112\"\n\"_631152000000000s\" \u2192 \"\ud835\udcaf\"", + "returns": null, + "existing_docstring": "Check if a target dimensionality can be derived from reference quantity dimensions.\n\nUses LINEAR ALGEBRA on the dimensional matrix - the same approach as\n_comprehensive_dimensional_analysis(). If the reference quantities span\nthe dimensional space (matrix rank = 4), any dimensionality can be derived.\n\nArgs:\n target_dim: Target Pint dimensionality object\n ref_dimensions: Dict mapping reference quantity names to their dimensionalities\n ureg: Pint UnitRegistry\n\nReturns:\n tuple: (can_derive, explanation_message)", "harvested_comments": [ - "Replace each constant with its display alias", - "Replace the constant name with display alias" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "The four fundamental dimensions", + "Build dimensional matrix from reference quantities (same as _comprehensive_dimensional_analysis)", + "Check matrix rank - if rank == 4, all fundamental dimensions are covered", + "Full coverage - any dimensionality can be derived", + "Partial coverage - identify which dimensions are missing" ], - "parent_class": "Model", + "status": "complete", + "needs": [], + "parent_class": null, "is_public": false }, { - "name": "_round_scale_for_conditioning", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1338, - "signature": "(self, scale: float, reference_mag: float = None) -> float", + "name": "_heuristic_scaling", + "kind": "function", + "file": "src/underworld3/utilities/nondimensional.py", + "line": 390, + "signature": "(var_name, dimensionality, fundamental_scales, reference_quantities)", "parameters": [ { - "name": "scale", - "type_hint": "float", + "name": "var_name", + "type_hint": null, "default": null, "description": "" }, { - "name": "reference_mag", - "type_hint": "float", - "default": "None", + "name": "dimensionality", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "fundamental_scales", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "reference_quantities", + "type_hint": null, + "default": null, "description": "" } ], - "returns": "float", - "existing_docstring": "Round scale to achieve optimal numerical conditioning based on unit_rounding_mode.\n\nThe goal is to choose a scale such that reference_mag / scale falls in a target range:\n- 'powers_of_10': Target [1, 10] (default) - optimal precision\n- 'engineering': Target [1, 1000] - SI prefix friendly, avoids fractions\n\nArgs:\n scale: The derived scale from dimensional analysis\n reference_mag: The reference quantity magnitude to condition (optional)\n\nReturns:\n Rounded scale value\n\nExamples:\n powers_of_10 mode (target result in [1, 10]):\n scale=500, ref=500 \u2192 100 (gives 500/100 = 5.0)\n scale=6.37e6, ref=6.37e6 \u2192 1e6 (gives 6.37e6/1e6 = 6.37)\n\n engineering mode (target result in [1, 1000]):\n scale=500, ref=500 \u2192 1 (gives 500/1 = 500)\n scale=6.37e6, ref=6.37e6 \u2192 1e6 (gives 6.37e6/1e6 = 6.37)", + "returns": null, + "existing_docstring": "Use heuristics based on variable name and type to determine scaling.\n\nArgs:\n var_name: Name of the variable (lowercase)\n dimensionality: Dimensionality string\n fundamental_scales: Dictionary of fundamental scales\n reference_quantities: Dictionary of reference quantities\n\nReturns:\n Scaling coefficient or None", "harvested_comments": [ - "If no reference magnitude, use old simple rounding", - "Define target range based on mode", - "Engineering: target [1, 1000], use powers of 1000", - "log10(1000)", - "powers_of_10 (default)" + "Velocity-like variables", + "Look for velocity reference", + "Fallback to L/T", + "Pressure-like variables", + "Look for pressure/stress reference" ], "status": "complete", "needs": [], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_round_to_nice_value", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1413, - "signature": "(self, magnitude: float) -> float", + "name": "_warn_if_ksp_diverged", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 34, + "signature": "(ksp, kind)", "parameters": [ { - "name": "magnitude", - "type_hint": "float", + "name": "ksp", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "kind", + "type_hint": null, "default": null, "description": "" } ], - "returns": "float", - "existing_docstring": "Simple rounding to nearest power of 10 or 1000 (fallback method).\n\nThis is used when we don't have a reference magnitude to condition against.", + "returns": null, + "existing_docstring": "Emit a rank-0 warning if a KSP finished on a KSP_DIVERGED_* reason\n(negative converged-reason). ``ksp.solve`` never raises on divergence, so\nwithout this the linear rotated-freeslip solvers here would return a\npartial answer to the caller silently \u2014 which is exactly what let the 3D\nrotation-nullspace bug (#306) go unnoticed until it produced clearly-wrong\nphysics in a downstream test.", "harvested_comments": [ - "Round to nearest power of 1000", - "powers_of_10", - "Round to nearest power of 10", - "Clean up floating point errors for values >= 1" + "306) go unnoticed until it produced clearly-wrong", + "Convention: > 0 == converged, < 0 == diverged, 0 == KSP_CONVERGED_ITERATING", + "(should not happen after ksp.solve() returns; warn if it does). Matches", + "petsc_generic_snes_solvers.pyx:2979." ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_generate_constant_name", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1441, - "signature": "(self, magnitude: float, si_units: str, dimension: str) -> str", + "name": "_point_coord", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 65, + "signature": "(dm, dim, cvec, csec, v0, v1, q)", "parameters": [ { - "name": "magnitude", - "type_hint": "float", + "name": "dm", + "type_hint": null, "default": null, "description": "" }, { - "name": "si_units", - "type_hint": "str", + "name": "dim", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "cvec", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "csec", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "v0", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "v1", + "type_hint": null, "default": null, "description": "" }, { - "name": "dimension", - "type_hint": "str", + "name": "q", + "type_hint": null, "default": null, "description": "" } ], - "returns": "str", - "existing_docstring": "Generate Pint _constant names following _100km pattern.", - "harvested_comments": [ - "Round to nice value first to avoid floating point precision issues", - "Simplify common SI units", - "Format magnitude for readability - ensure valid Python identifiers", - "IMPORTANT: Avoid using recognizable unit names (like \"km\", \"m\", \"kg\") in constant names!", - "Pint tries to parse them and fails. Use separators to prevent parsing." - ], + "returns": null, + "existing_docstring": "Coordinate of a DMPlex point (vertex \u2192 its coord; higher point \u2192 mean of\nits closure vertices).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_convert_to_user_time_unit", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1483, - "signature": "(self, time_base_units, velocity_quantity)", + "name": "_boundary_velocity_nodes", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 75, + "signature": "(solver, boundary, normal = None)", "parameters": [ { - "name": "time_base_units", + "name": "solver", "type_hint": null, "default": null, "description": "" }, { - "name": "velocity_quantity", + "name": "boundary", "type_hint": null, "default": null, "description": "" + }, + { + "name": "normal", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Infer appropriate time unit from velocity reference quantity.\n\nThis implements Stage 2 of the two-stage simplification:\nAfter rationalizing to SI base units, convert to user-friendly time units\nby inferring the appropriate scale from the velocity's unit system.\n\nParameters\n----------\ntime_base_units : pint.Quantity\n Time in SI base units (seconds)\nvelocity_quantity : pint.Quantity\n The velocity quantity used to derive time (contains unit hints)\n\nReturns\n-------\npint.Quantity\n Time in user-appropriate units (year, megayear, day, etc.)\n\nExamples\n--------\n>>> # If velocity is in cm/year, time should be in years or megayears\n>>> time_sec = 1.26e14 * ureg.second\n>>> velocity = 5 * ureg.cm / ureg.year\n>>> result = model._convert_to_user_time_unit(time_sec, velocity)\n>>> # result: 40.0 megayear (not 1.26e14 second)", + "existing_docstring": "DMPlex points carrying velocity DOFs on `boundary`, each with an outward\nunit normal. Dimension-general (2D edges, 3D faces).\n\n`normal` selects the normal source (per boundary):\n * None \u2014 geometric facet normal from PETSc ``computeCellGeometryFVM``\n (area-weighted, accumulated to the facet closure points).\n * sympy 1\u00d7dim Matrix \u2014 an analytic normal (function of mesh.X); evaluated\n at each node's coordinate. Best for exact curved/planar faces\n (radial ``X/|X|`` on a spherical cap; a constant on a planar\n side of a regional spherical box).\n * (dim,) array \u2014 a constant normal vector.\nReturns ``[(point, n\u0302), ...]``.", "harvested_comments": [ - "If velocity is in cm/year, time should be in years or megayears", - "result: 40.0 megayear (not 1.26e14 second)", - "Infer time unit from velocity's time component", - "Geological time scale", - "Daily time scale" + "Boundary facets via the consolidated \"UW_Boundaries\" label (per-boundary labels do", + "not survive mesh adaptation); raises a clear error for an unknown boundary name.", + "In parallel a rank may own NO part of this boundary \u2192 a null IS; guard and return", + "no local nodes (calling getIndices() on a null IS would segfault).", + "facets (edges in 2D, faces in 3D)" ], - "status": "complete", - "needs": [], - "parent_class": "Model", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, "is_public": false }, { - "name": "_choose_display_units", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 1552, - "signature": "(self, quantity, dimension_name = '[unknown]')", + "name": "_finalize_rotated_solution", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 337, + "signature": "(solver, U, Q, normal_rows, remove_rotation_gauge)", "parameters": [ { - "name": "quantity", + "name": "solver", "type_hint": null, "default": null, "description": "" }, { - "name": "dimension_name", + "name": "U", "type_hint": null, - "default": "'[unknown]'", + "default": null, + "description": "" + }, + { + "name": "Q", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "normal_rows", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "remove_rotation_gauge", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Choose magnitude-appropriate units for display.\n\nThis implements the display-time unit selection strategy: choose units\nthat give values between 0.1 and 1000 for better readability.\n\nParameters\n----------\nquantity : pint.Quantity\n Quantity to display in appropriate units\ndimension_name : str, optional\n Dimension name for context (e.g., '[length]', '[time]', '[mass]')\n\nReturns\n-------\npint.Quantity\n Same quantity in magnitude-appropriate units\n\nExamples\n--------\n>>> # Length: 2e6 m \u2192 2000 km\n>>> length = 2e6 * ureg.meter\n>>> display_length = model._choose_display_units(length, '[length]')\n>>> # display_length: 2000 kilometer\n\n>>> # Time: 1.26e14 s \u2192 40 Myr\n>>> time = 1.26e14 * ureg.second\n>>> display_time = model._choose_display_units(time, '[time]')\n>>> # display_time: 40.0 megayear", + "existing_docstring": "Remove the rigid-rotation gauge (if it is a genuine null space of the\nconstrained problem), scatter the composite global vector ``U`` into the\nvelocity/pressure fields, and refresh the enhanced-variable caches. Shared by\nthe linear one-shot and the nonlinear driver. Returns whether the gauge was\nremoved.", "harvested_comments": [ - "Length: 2e6 m \u2192 2000 km", - "display_length: 2000 kilometer", - "Time: 1.26e14 s \u2192 40 Myr", - "display_time: 40.0 megayear", - "Get base magnitude for comparison" + "Remove the rigid-rotation gauge \u2014 every mode that is a genuine null space of", + "the constrained problem (closed circular free-slip: one; full spherical", + "shell: all three); on straight walls the constraint pins the rotations, and", + "projecting would corrupt the solution.", + "Done on the GLOBAL vector U with PETSc dots (parallel-correct ownership \u2014 a" ], - "status": "complete", - "needs": [], - "parent_class": "Model", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, "is_public": false }, { - "name": "_format_scale_representations", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2143, - "signature": "(self, scale_qty) -> str", + "name": "_zero_rows_local", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 394, + "signature": "(vec, normal_rows)", "parameters": [ { - "name": "scale_qty", + "name": "vec", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "normal_rows", "type_hint": null, "default": null, "description": "" } ], - "returns": "str", - "existing_docstring": "Format a scale quantity showing three representations:\n1. Derivation form (how it was derived)\n2. SI base units (simplified)\n3. Model base units (what 1.0 represents in the model)\n\nParameters\n----------\nscale_qty : pint.Quantity\n The scale quantity to format\n\nReturns\n-------\nstr\n Formatted string with all three representations", - "harvested_comments": [ - "1. Derivation form (current complex units)", - "2. SI base units (simplified)", - "3. Model base units (what 1.0 represents)", - "The scale quantity itself represents what \"1.0\" means in model units", - "So we format it in a clear way to show this" + "returns": null, + "existing_docstring": "Zero ``vec`` at the global rows ``normal_rows`` using ownership-relative\nlocal indices (indexing the local slice with global rows overflows on any rank\nwhose ownership does not start at 0 \u2014 the np>1 crash class).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_get_domain_friendly_scale", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2318, - "signature": "(self, qty)", + "name": "_gather_fields_to_global", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 403, + "signature": "(solver)", "parameters": [ { - "name": "qty", + "name": "solver", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Choose domain-appropriate units based on magnitude using Pint's capabilities.", - "harvested_comments": [ - "Use the SHARED underworld3 unit registry to avoid registry mismatch errors", - "Length scales - choose appropriate geological/engineering units", - "Time scales - choose appropriate geological/engineering units", - "Mass scales", - "Very large masses" - ], + "existing_docstring": "Composite global vector built from the solver's current velocity/pressure\nfield values (the warm-start initial guess for the nonlinear driver).", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_suggest_reference_quantities", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2539, - "signature": "(self, missing_dimensions) -> list", + "name": "_build_rotated_custom_Pl", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 616, + "signature": "(solver, Q, normal_rows)", "parameters": [ { - "name": "missing_dimensions", + "name": "solver", "type_hint": null, "default": null, "description": "" - } - ], - "returns": "list", - "existing_docstring": "Suggest specific reference quantities to complete missing dimensions.\n\nParameters\n----------\nmissing_dimensions : list\n List of dimension names that are missing\n\nReturns\n-------\nlist\n List of suggestion strings for each missing dimension", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": false - }, - { - "name": "_handle_conversion_failure", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2603, - "signature": "(self, qty, qty_dimensionality)", - "parameters": [ + }, { - "name": "qty", + "name": "Q", "type_hint": null, "default": null, "description": "" }, { - "name": "qty_dimensionality", + "name": "normal_rows", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Handle conversion failure with diagnostic error messages and suggestions.\n\nParameters\n----------\nqty : pint.Quantity\n The quantity that failed to convert\nqty_dimensionality : pint.util.UnitsContainer\n The dimensionality of the failed quantity\n\nReturns\n-------\nUWQuantity\n Dimensionless quantity with original magnitude (fallback)\n\nRaises\n------\nValueError\n With diagnostic information when strict error handling is enabled", - "harvested_comments": [ - "Analyze what dimensions are needed vs. available", - "Convert Pint internal dimension to our naming", - "Get validation results", - "Build diagnostic message", - "For now, issue a warning instead of raising an error for backward compatibility" + "existing_docstring": "The rotated custom-FMG prolongation list [*coarse, Q_v\u00b7P_fine] for the\nvelocity block, or None if no hierarchy is registered. Depends only on Q and\nthe mesh (NOT the solution), so the nonlinear driver builds it ONCE and reuses\nit across Newton iterations (the prolongation build is the expensive part).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_optimize_scales_for_readability", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2718, - "signature": "(self, scalings)", + "name": "_pressure_mass_schur_pmat", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 633, + "signature": "(solver)", "parameters": [ { - "name": "scalings", + "name": "solver", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Optimize scales to use nice round numbers while keeping reference quantities O(1).\n\nParameters\n----------\nscalings : dict\n Dictionary of fundamental scalings from derive_fundamental_scalings()\n\nReturns\n-------\ndict\n Optimized scalings with nice round numbers", + "existing_docstring": "The native 1/mu pressure-mass Schur preconditioner block, extracted from the\nsolver's assembled Pmat \u2014 the SAME p-p block the standard path uses via\n``pc_fieldsplit_schur_precondition=a11`` (the DS JacobianPreconditioner term\n``_pp_G0``). Q is identity on pressure, so the block needs no rotation.\nReturns None (\u2192 selfp fallback) when the Pmat is not distinct from the operator\nor the block was not assembled. The CALLER must have assembled the Pmat\n(``snes.computeJacobian(x, J, Jp)``) and owns the returned Mat.", "harvested_comments": [ - "Find nearest \"nice\" scale", - "Import units backend to create new quantity", - "Fallback to original scale if optimization fails", - "Non-standard scale format, keep as-is" + "not assembled \u2192 useless" ], - "status": "complete", - "needs": [], - "parent_class": "Model", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, "is_public": false }, { - "name": "_find_nice_scale", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2754, - "signature": "(self, value)", + "name": "_velocity_diag_scale", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 652, + "signature": "(Ahat, solver)", "parameters": [ { - "name": "value", + "name": "Ahat", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Find nearest 'nice' number (powers of 10 times 1, 2, or 5).\n\nParameters\n----------\nvalue : float\n Original scale value\n\nReturns\n-------\nfloat\n Nearest nice scale value\n\nExamples\n--------\n>>> model._find_nice_scale(2900) # \u2192 1000 or 5000\n>>> model._find_nice_scale(0.38) # \u2192 0.5 or 0.2\n>>> model._find_nice_scale(15.7) # \u2192 10 or 20", - "harvested_comments": [ - "\u2192 1000 or 5000", - "\u2192 0.5 or 0.2", - "Get the order of magnitude", - "Choose from nice mantissas: 1, 2, 5", - "Include 10 to handle edge cases" - ], - "status": "complete", - "needs": [], - "parent_class": "Model", - "is_public": false - }, - { - "name": "_detect_scaling_conflicts", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 2800, - "signature": "(self, ref_qty)", - "parameters": [ + }, { - "name": "ref_qty", + "name": "solver", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Detect conflicts in over-determined dimensional systems.\n\nParameters\n----------\nref_qty : dict\n Reference quantities dictionary\n\nReturns\n-------\ndict\n Dictionary with keys:\n - 'has_conflicts': bool indicating if conflicts were found\n - 'conflicts': list of conflict descriptions\n - 'resolutions': list of suggested resolutions\n - 'redundant_quantities': list of quantity names that could be removed", - "harvested_comments": [ - "Import units (Pint) for dimensional analysis", - "Build a map of what dimensions each quantity provides", - "Track which quantities can provide each base dimension", - "Check for direct conflicts (multiple quantities of same dimension)", - "Keep first, mark others as redundant" + "existing_docstring": "Mean |diag| of the (rotated) velocity block \u2014 the representative diagonal\nfor constraint rows (see the zeroRowsColumns call sites). Collective.", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_convert_to_model_units_general", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 3375, - "signature": "(self, quantity)", + "name": "_destroy_rotated_ksp_ctx", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 665, + "signature": "(ctx)", "parameters": [ { - "name": "quantity", + "name": "ctx", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert any quantity to model units using Pint's native conversion.\n\nReturns None for dimensionless quantities gracefully.", - "harvested_comments": [ - "NEW APPROACH: Use Pint's native .to() method instead of manual dimensional analysis", - "This avoids double-scaling issues and leverages Pint's unit conversion engine", - "IMPORTANT: The source quantity might be in a different Pint registry!", - "We need to reconstruct it in the MODEL's registry before converting.", - "First get magnitude and units from the input" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Model", - "is_public": false - }, - { - "name": "__repr__", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 4318, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Override Pydantic's __repr__ for better user experience.", - "harvested_comments": [ - "Add units information" - ], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" - ], - "parent_class": "Model", - "is_public": false - }, - { - "name": "__str__", - "kind": "method", - "file": "src/underworld3/model.py", - "line": 4335, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "String representation for print() calls.", + "existing_docstring": "Release the reusable rotated-KSP context (KSP and the owned Schur pmat).", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Model", + "parent_class": null, "is_public": false }, { - "name": "_should_rank_execute", + "name": "_solve_rotated_iterative", "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 58, - "signature": "(current_rank, rank_selector, total_size)", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 675, + "signature": "(solver, Ahat, bhat, Q, Qt, normal_rows, verbose = False, custom_Pl = None, nsp = None, Mp = None, ctx = None)", "parameters": [ { - "name": "current_rank", + "name": "solver", "type_hint": null, "default": null, "description": "" }, { - "name": "rank_selector", + "name": "Ahat", "type_hint": null, "default": null, "description": "" }, { - "name": "total_size", + "name": "bhat", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Determine if a rank should execute based on rank selector.\n\nArgs:\n current_rank: The rank to check\n rank_selector: int, slice, list, tuple, callable, str, or numpy array\n total_size: Total number of ranks\n\nReturns:\n bool: True if rank should execute", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": false - }, - { - "name": "_get_executing_ranks", - "kind": "function", - "file": "src/underworld3/mpi.py", - "line": 109, - "signature": "(rank_selector, total_size)", - "parameters": [ + }, { - "name": "rank_selector", + "name": "Q", "type_hint": null, "default": null, "description": "" }, { - "name": "total_size", + "name": "Qt", "type_hint": null, "default": null, "description": "" - } - ], - "returns": null, - "existing_docstring": "Get set of ranks that will execute for a given selector.\n\nArgs:\n rank_selector: Rank selection specification\n total_size: Total number of ranks\n\nReturns:\n set: Set of rank numbers that will execute", - "harvested_comments": [], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": false - }, - { - "name": "_create_variable_array", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 378, - "signature": "(self, initial_data = None)", - "parameters": [ + }, { - "name": "initial_data", + "name": "normal_rows", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "verbose", + "type_hint": null, + "default": "False", + "description": "" + }, + { + "name": "custom_Pl", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "nsp", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "Mp", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "ctx", "type_hint": null, "default": "None", "description": "" } ], "returns": null, - "existing_docstring": "Factory function to create NDArray_With_Callback for variable data.\nFollows the same pattern as swarm.points implementation.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Array object with callback for automatic PETSc synchronization", + "existing_docstring": "Solve the rotated saddle with a SELF-CONTAINED fieldsplit-Schur KSP on the\nrotated operator. The velocity block is geometric FMG on the CUSTOM prolongation\n(PR#290, rotated) when a hierarchy is registered (``set_custom_fmg``), else GAMG\n(tuned to the native path's settings).\n\nA plain rotated Mat has no DM field info, so UW3's DM-coupled fieldsplit cannot\nsplit it \u2014 we build the split from EXPLICIT velocity/pressure index sets. For the\ncustom-FMG case the velocity sub-PC gets our prolongation via ``setMGInterpolation``\n(needs no DM); the rotated block A_vv = Q_v A_vv Q_v\u1d40 is formed from \u00c2hat\nautomatically and only the FINE prolongation is rotated (Galerkin coarse ops\nauto-correct). NO direct solve of the fine system.\n\nPreconditioning parity with the native solve (the Schur-iteration fix):\n * ``Mp`` \u2014 the 1/mu pressure-mass block from the native Pmat\n (``_pressure_mass_schur_pmat``) is installed as a USER Schur preconditioner,\n exactly what the standard path uses via ``schur_precondition=a11``. Without\n it (Mp=None) the Schur pre falls back to selfp, which degrades badly on\n curved/deformed boundaries and variable viscosity.\n * the pressure sub-solve mirrors the native FGMRES+GASM at the solver\n tolerance; the constant-pressure nullspace is attached to the Schur\n complement for enclosed domains (the hand-built IS fieldsplit does not\n inherit it from the operator).\n\n``custom_Pl`` / ``nsp`` may be PREBUILT (nonlinear driver: build once, reuse each\nNewton step); when None they are built here (linear one-shot).\n\nReturns ``(Uhat, reason, ctx)``. Passing ``ctx`` back in reuses the KSP/PC\nacross Newton iterations \u2014 the fieldsplit ISs, Schur USER pmat and FMG\nprolongations survive; only the operator-values refresh is paid. The caller\nmust keep ``Ahat``/``Mp`` the SAME Mat objects (values updated in place) and\nrelease the context with ``_destroy_rotated_ksp_ctx`` when done.", "harvested_comments": [ - "Create NDArray_With_Callback (following swarm._points pattern)", - "Allow operations like existing arrays", - "Single callback function (following swarm_update_callback pattern)", - "Only act on data-changing operations (following swarm.points pattern)", - "Skip updates during coordinate changes to prevent corruption" + "290, rotated) when a hierarchy is registered (``set_custom_fmg``), else GAMG", + "rotated coupled null space (pressure-const \u2295 Q\u00b7rotation) on the operator", + "UNIQUE prefix per KSP (see _ROT_SOLVE_COUNT) so concurrent rotated solves", + "do not share global-options state; the keys are removed after setup.", + "native-parity pressure sub-solve (pyx Stokes defaults): FGMRES at the" ], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, "is_public": false }, { - "name": "_create_canonical_data_array", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 422, - "signature": "(self, initial_data = None)", + "name": "_rotated_nullspace", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 844, + "signature": "(solver, Q, normal_rows)", "parameters": [ { - "name": "initial_data", + "name": "solver", "type_hint": null, - "default": "None", + "default": null, + "description": "" + }, + { + "name": "Q", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "normal_rows", + "type_hint": null, + "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Create the single canonical data array with PETSc synchronization.\nThis is the ONLY method that creates arrays with PETSc callbacks.\n\nReturns data in shape (-1, num_components) using pack_raw/unpack_raw methods.\n\nParameters\n----------\ninitial_data : numpy.ndarray, optional\n Initial data for the array. If None, fetches current data from PETSc.\n\nReturns\n-------\nNDArray_With_Callback\n Canonical array object with callback for automatic PETSc synchronization", + "existing_docstring": "Coupled Stokes null space in the rotated frame: constant pressure, plus every\nrigid rotation Q\u00b7mode that is a genuine null space of the constraints \u2014 one\nmode (-y,x) in 2D, up to THREE (e_k\u00d7r) in 3D (all three on a spherical shell\nwith free-slip inner and outer boundaries; leaving them off makes the outer\nKrylov grind against a near-singular operator and pollutes the solution with\narbitrary rotation content). Returns a PETSc.NullSpace on the composite\nvector, or None.\n\nEach vector is ZEROED at the constrained normal rows so it is exactly compatible\nwith the strong v_n=0 constraint. Without this, a rotation-mode vector that is\nonly ~O(h\u00b2)-small at the constrained rows (curved boundary: the normal in Q is\nsampled at the chord midpoint, the rotation at the true P2 node) would, when\nPETSc projects the solution onto the null space, inject a spurious wall-normal\ncomponent \u2014 a strong BC must be exact, independent of the iterative solve.", "harvested_comments": [ - "Use unpack_raw to get flat format (-1, num_components)", - "Handle case where unpack returns None (swarm not initialized)", - "Create NDArray_With_Callback for flat data", - "Allow operations like existing arrays", - "Single canonical callback for PETSc synchronization" + "constant pressure (Q = identity on pressure \u2192 unchanged)", + "persists inside the returned NullSpace", + "rigid rotations (rotated), each only if it satisfies the constraints.", + "COLLECTIVE: all ranks walk the same mode list, same order.", + "tr persists in the NullSpace" ], - "status": "complete", - "needs": [], - "parent_class": "SwarmVariable", - "is_public": false - }, - { - "name": "_create_array_view", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 490, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Create array view of canonical data using appropriate conversion strategy.\n\nStrategy depends on variable complexity:\n- Scalars/Vectors: Simple reshape operations\n- 2D+ Tensors: Complex pack/unpack operations\n\nReturns\n-------\nArrayView\n Array-like object that delegates changes back to canonical data", - "harvested_comments": [], "status": "partial", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", + "parent_class": null, "is_public": false }, { - "name": "_is_simple_variable", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 508, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Check if this is a simple scalar/vector variable (not a complex tensor)", - "harvested_comments": [], - "status": "minimal", - "needs": [ - "NEEDS_PARAMETERS" + "name": "_mode_satisfies_constraints", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 894, + "signature": "(solver, Q, normal_rows, tg, tol = 1e-08)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "Q", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "normal_rows", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tg", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "tol", + "type_hint": null, + "default": "1e-08", + "description": "" + } ], - "parent_class": "SwarmVariable", - "is_public": false - }, - { - "name": "_create_simple_array_view", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 512, - "signature": "(self)", - "parameters": [], "returns": null, - "existing_docstring": "Array view for scalars/vectors using simple reshape operations", + "existing_docstring": "True iff the rigid-body mode ``tg`` satisfies all rotated v_n=0\nconstraints \u2014 i.e. Q\u00b7tg is ~0 on every constrained normal row. (A closed\ncircular boundary admits its one rotation; a full spherical shell admits all\nthree; straight/partial walls pin them.)\n\nCOLLECTIVE: every rank runs the same global-vector ops. Do NOT early-return on\na per-rank ``not normal_rows`` \u2014 in parallel a rank may own no boundary node\n(empty normal_rows) while others do, and an early return there would desync the\ncollective norms below and deadlock.", "harvested_comments": [ - "Simple reshape: (-1, num_components) -> (N, a, b)", - "For simple variables, reshape to (N, a, b) format", - "Apply dimensionalization if needed", - "Check if variable has units and model has reference quantities", - "Variable has units - wrap with UnitAwareArray" + "parallel norm", + "norm of tr restricted to the constrained rows: zero everything else, then .norm()", + "parallel (collective on all ranks)", + "transient duplicates" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SwarmVariable", + "parent_class": null, "is_public": false }, { - "name": "_create_tensor_array_view", - "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 722, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Array view for complex tensors using pack/unpack operations", - "harvested_comments": [ - "Use complex pack/unpack for tensor layouts", - "Apply dimensionalization if needed", - "Check if variable has units and model has reference quantities", - "Variable has units - wrap with UnitAwareArray", - "If ND scaling is active, data is non-dimensional and needs dimensionalization" + "name": "_rigid_rotation_modes", + "kind": "function", + "file": "src/underworld3/utilities/rotated_bc.py", + "line": 1005, + "signature": "(solver)", + "parameters": [ + { + "name": "solver", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "minimal", + "returns": null, + "existing_docstring": "The Cartesian rigid-body rotation mode(s) as composite GLOBAL vectors\n(velocity DOFs only, zero pressure): ``[(-y,x)]`` in 2D, ``[e_k\u00d7r]`` for\nk=x,y,z in 3D \u2014 a spherical shell with free-slip on both boundaries has all\nTHREE. Parallel-safe: built via localToGlobal on the velocity sub-DM, so\nshared nodes are handled by PETSc, not double-counted. The vectors are\nborrowed from the DM pool \u2014 every one must go back via\n``dm.restoreGlobalVec``. COLLECTIVE: all ranks build the same mode list.", + "harvested_comments": [], + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "parent_class": "SwarmVariable", + "parent_class": null, "is_public": false }, { - "name": "_pack_array_to_data_format", + "name": "__new__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 929, - "signature": "(self, array_data)", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 63, + "signature": "(cls, input_array = None, units = None, owner = None, callback = None, unit_checking = True, auto_convert = True, **kwargs)", "parameters": [ { - "name": "array_data", + "name": "input_array", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "owner", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "callback", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "unit_checking", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "auto_convert", + "type_hint": null, + "default": "True", + "description": "" + }, + { + "name": "**kwargs", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Convert array format (N,a,b) back to canonical data format (N,components)", + "existing_docstring": "Create new UnitAwareArray instance.\n\nParameters\n----------\ninput_array : array-like, optional\n Input data to create array from\nunits : str or UWQuantity, optional\n Units for this array (e.g., \"m\", \"m/s\", \"kg\")\nowner : object, optional\n The object that owns this array (stored as weak reference)\ncallback : callable, optional\n Initial callback function to register\nunit_checking : bool, optional\n If True, enforce unit compatibility in operations (default True)\nauto_convert : bool, optional\n If True, automatically convert compatible units (default True)\n**kwargs : dict\n Additional arguments passed to NDArray_With_Callback", "harvested_comments": [ - "Use existing pack logic but return numpy array instead of writing to PETSc", - "This is a pure conversion method - no PETSc access" + "Create the NDArray_With_Callback instance", + "Initialize unit tracking", + "Store original for reference", + "Set units if provided" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_update", + "name": "__array_finalize__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1021, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 108, + "signature": "(self, obj)", + "parameters": [ + { + "name": "obj", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Mark proxy mesh variable as stale for lazy evaluation.\nThe actual update happens when the proxy is accessed.", + "existing_docstring": "Called whenever the system allocates a new array from this template.", "harvested_comments": [ - "if not proxied, nothing to do. return.", - "Mark proxy as stale for lazy evaluation (avoids immediate PETSc access conflicts)" + "Call parent finalize first", + "Copy unit information from parent array" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_update_proxy_if_stale", + "name": "_set_units", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1036, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 122, + "signature": "(self, units)", + "parameters": [ + { + "name": "units", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Actually update the proxy mesh variable if it's marked as stale.\nThis implements lazy evaluation to avoid PETSc access conflicts.", + "existing_docstring": "Internal method to set units for this array.\n\nParameters\n----------\nunits : str, Pint Unit, or UWQuantity\n Units specification", "harvested_comments": [ - "if not proxied, nothing to do. return.", - "Only update if stale and not already updating", - "Mark as fresh" + "Parse string to Pint Unit (POLICY: store Pint Units, not strings)", + "\u2705 Pint Unit", + "Already a Pint Unit object - store directly", + "\u2705 Pint Unit", + "Extract units from UWQuantity or similar" ], - "status": "minimal", + "status": "partial", "needs": [ - "NEEDS_PARAMETERS" + "NEEDS_RETURNS" ], - "parent_class": "SwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_rbf_to_meshVar", + "name": "_check_unit_compatibility", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1060, - "signature": "(self, meshVar, nnn = None, verbose = False)", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 268, + "signature": "(self, other, operation = 'operation')", "parameters": [ { - "name": "meshVar", + "name": "other", "type_hint": null, "default": null, "description": "" }, { - "name": "nnn", - "type_hint": null, - "default": "None", - "description": "" - }, - { - "name": "verbose", + "name": "operation", "type_hint": null, - "default": "False", + "default": "'operation'", "description": "" } ], "returns": null, - "existing_docstring": "Here is how it works: for each particle, create a distance-weighted average on the node data\n\nTodo: caching the k-d trees etc for the proxy-mesh-variable nodal points\nTodo: some form of global fall-back for when there are no particles on a processor", + "existing_docstring": "Check unit compatibility with another array or value.\n\nParameters\n----------\nother : array-like or scalar\n Other operand to check compatibility with\noperation : str\n Name of operation for error messages\n\nReturns\n-------\ntuple\n (compatible: bool, converted_other: array-like, result_units: str)", "harvested_comments": [ - "Mapping to the coordinates of the variable from the", - "particle coords", - "If this is our own proxy variable and mesh has changed, recreate it", - "Use the newly created proxy variable" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "Handle scalar values (dimensionless)", + "Initialize other_units to None", + "Check if other has units", + "Could be UnitAwareArray, Pint Quantity, or UWQuantity", + "Convert to string if it's a Pint Unit object" ], - "parent_class": "SwarmVariable", + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_rbf_reduce_to_meshVar", + "name": "_wrap_result", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1091, - "signature": "(self, meshVar, verbose = False)", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 447, + "signature": "(self, result, units = '__unspecified__')", "parameters": [ { - "name": "meshVar", + "name": "result", "type_hint": null, "default": null, "description": "" }, { - "name": "verbose", + "name": "units", "type_hint": null, - "default": "False", + "default": "'__unspecified__'", "description": "" } ], "returns": null, - "existing_docstring": "This method updates a mesh variable for the current\nswarm & particle variable state by reducing the swarm to\nthe nearest point for each particle\n\nHere is how it works:\n\n 1) for each particle, create a distance-weighted average on the node data\n 2) check to see which nodes have zero weight / zero contribution and replace with nearest particle value\n\nTodo: caching the k-d trees etc for the proxy-mesh-variable nodal points\nTodo: some form of global fall-back for when there are no particles on a processor", + "existing_docstring": "Wrap operation result as UnitAwareArray with appropriate units.\n\nParameters\n----------\nresult : array-like\n Result of operation\nunits : str or None, optional\n Units for the result. If None, result is dimensionless (plain array).\n If not provided, defaults to self._units.\n\nReturns\n-------\nUnitAwareArray, ndarray, or scalar\n Wrapped result with units (or plain array if dimensionless)", "harvested_comments": [ - "if not proxied, nothing to do. return.", - "1 - Average particles to nodes with distance weighted average", - "Use non-dimensional coordinates for internal KDTree (matches swarm.data coordinate system)", - "need actual distances", - "2 - set NN vals on mesh var where w == 0.0" - ], - "status": "partial", - "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "Scalar results don't need unit tracking", + "Determine final units", + "If dimensionless (units explicitly set to None), return plain array", + "Preserve as UnitAwareArray with units" ], - "parent_class": "SwarmVariable", + "status": "complete", + "needs": [], + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_object_viewer", + "name": "_wrap_scalar_result", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 1358, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "This will substitute specific information about this object", - "harvested_comments": [ - "feedback on this instance" + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 489, + "signature": "(self, value)", + "parameters": [ + { + "name": "value", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Wrap scalar result with units as UWQuantity.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "SwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_update", + "name": "__add__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2010, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1103, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Backward compatibility wrapper for _update_proxy_variables.\n\nMaintains existing API while implementing lazy evaluation internally.", + "existing_docstring": "Addition with unit compatibility checking.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_on_data_changed", + "name": "__radd__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2018, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1113, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Hook called by unified data callback when canonical data changes.\n\nFor IndexSwarmVariable, this marks proxy variables as stale for lazy evaluation.\nThis replaces the complex custom array override with a simple hook.", + "existing_docstring": "Right addition with unit compatibility checking.", "harvested_comments": [], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_update_proxy_variables", + "name": "__sub__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2095, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "This method updates the proxy mesh (vector) variable for the index variable on the current swarm locations\n\nHere is how it works:\n\n 1) for each particle, create a distance-weighted average on the node data\n 2) for each index in the set, we create a mask mesh variable by mapping 1.0 wherever the\n index matches and 0.0 where it does not.\n\nNOTE: If no material is identified with a given nodal value, the default is to impose\na near-neighbour hunt for a valid material and set that one\n\n## ToDo: This should be revisited to match the updated master copy of _update\n\nupdate_type 0: assign the particles to the nearest mesh_levelset nodes, and calculate the value on nodes from them.\nupdate_type 1: calculate the material property value on mesh_levelset nodes from the nearest N particles directly.", - "harvested_comments": [ - "# ToDo: This should be revisited to match the updated master copy of _update", - "Use non-dimensional coordinates for internal level set KDTree", - "n, d, b = kd_swarm.find_closest_point(self._meshLevelSetVars[0].coords)", - "if there is no material found,", - "impose a near-neighbour hunt for a valid material and set that one" + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1123, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } ], - "status": "partial", + "returns": null, + "existing_docstring": "Subtraction with unit compatibility checking.", + "harvested_comments": [], + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "IndexSwarmVariable", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "__del__", + "name": "__rsub__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 2415, - "signature": "(self)", - "parameters": [], - "returns": null, - "existing_docstring": "Cleanup swarm by unregistering from mesh to prevent memory leaks", - "harvested_comments": [ - "Mesh/Model may have already been garbage collected, which is fine" + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1135, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } ], + "returns": null, + "existing_docstring": "Right subtraction with unit compatibility checking.", + "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_force_migration_after_mesh_change", + "name": "__mul__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3160, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1147, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Force migration of swarm particles after mesh coordinate changes.\n\nThis method bypasses the normal migration_disabled check since mesh\ncoordinate changes require swarm particles to be re-distributed\nregardless of migration disabled state.", + "existing_docstring": "Multiplication with unit handling.", "harvested_comments": [ - "Temporarily override migration disabled state", - "Disable variable array callbacks during migration to prevent corruption", - "Collect all variable arrays and disable their callbacks", - "Perform standard migration", - "Re-enable variable array callbacks" + "CRITICAL FIX (2025-11-27): Handle Pint Quantity multiplication properly.", + "UnitAwareArray * Pint Quantity: Must combine units correctly.", + "Combine units: UnitAwareArray units \u00d7 Pint units", + "Multiply numeric values", + "Scalar multiplication preserves units" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_PARAMETERS", - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_legacy_access", + "name": "__rmul__", "kind": "method", - "file": "src/underworld3/swarm.py", - "line": 3759, - "signature": "(self, *writeable_vars)", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1174, + "signature": "(self, other)", "parameters": [ { - "name": "*writeable_vars", - "type_hint": "SwarmVariable", + "name": "other", + "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "This context manager makes the underlying swarm variables data available to\nthe user. The data should be accessed via the variables `data` handle.\n\nAs default, all data is read-only. To enable writeable data, the user should\nspecify which variable they wish to modify.\n\nAt the conclusion of the users context managed block, numerous further operations\nwill be automatically executed. This includes swarm parallel migration routines\nwhere the swarm's `particle_coordinates` variable has been modified. The swarm\nvariable proxy mesh variables will also be updated for modifed swarm variables.\n\nParameters\n----------\nwriteable_vars\n The variables for which data write access is required.\n\nExample\n-------\n\n>>> import underworld3 as uw\n>>> someMesh = uw.discretisation.FeMesh_Cartesian()\n>>> with someMesh._deform_mesh():\n... someMesh.data[0] = [0.1,0.1]\n>>> someMesh.data[0]\narray([ 0.1, 0.1])", + "existing_docstring": "Right multiplication with unit handling.", "harvested_comments": [ - "if already accessed within higher level context manager, continue.", - "set flag so variable status can be known elsewhere", - "add to de-access list to rewind this later", - "grab numpy object, setting read only if necessary", - "increment variable state" + "CRITICAL FIX (2025-11-27): Handle Pint Quantity multiplication properly.", + "Pint Quantity * UnitAwareArray: Pint sees UnitAwareArray as plain array,", + "so it returns a Pint Quantity with wrong units. We intercept here.", + "Combine units: Pint units \u00d7 UnitAwareArray units", + "Multiply numeric values" ], - "status": "partial", + "status": "minimal", "needs": [ - "NEEDS_RETURNS" + "NEEDS_PARAMETERS" ], - "parent_class": "Swarm", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_setup_projections", + "name": "__truediv__", "kind": "method", - "file": "src/underworld3/systems/ddt.py", - "line": 350, - "signature": "(self)", - "parameters": [], + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1202, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Initialize projection solvers for history updates.", + "existing_docstring": "Division with unit handling.", "harvested_comments": [ - "## using this to store terms that can't be evaluated (e.g. derivatives)", - "The projection operator for mapping derivative values to the mesh - needs to be different for each variable type, unfortunately ..." + "Scalar division preserves units" ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": "Eulerian", + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_class_timer_decorator", - "kind": "function", - "file": "src/underworld3/timing.py", - "line": 318, - "signature": "(cls)", - "parameters": [], + "name": "__rtruediv__", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1216, + "signature": "(self, other)", + "parameters": [ + { + "name": "other", + "type_hint": null, + "default": null, + "description": "" + } + ], "returns": null, - "existing_docstring": "Decorator that adds timing to all methods in a class.\n\nWalks through class methods and wraps them with routine_timer_decorator.\n\nParameters\n----------\ncls : type\n Class to decorate\n\nReturns\n-------\ntype\n Same class with methods wrapped for timing\n\nExample\n-------\n>>> @uw.timing._class_timer_decorator\n>>> class MyAnalysis:\n>>> def compute(self):\n>>> # ... work ...\n>>>\n>>> uw.timing.start()\n>>> analysis = MyAnalysis()\n>>> analysis.compute() # Automatically timed\n>>> uw.timing.print_table()", + "existing_docstring": "Right division with unit handling.", "harvested_comments": [ - "... work ...", - "Automatically timed", - "Skip special methods", - "Skip already decorated", - "Create timed version" + "Scalar division - units are inverted" ], - "status": "complete", - "needs": [], - "parent_class": null, - "is_public": false - }, - { - "name": "_get_ureg", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 43, - "signature": "()", - "parameters": [], - "returns": null, - "existing_docstring": "Get the Pint unit registry.", - "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_get_pint_helper", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 82, - "signature": "()", + "name": "__repr__", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1230, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "Get the Pint helper (lazy singleton).", - "harvested_comments": [], + "existing_docstring": "String representation including units.", + "harvested_comments": [ + "Insert units info before the closing parenthesis of dtype info", + "Find dtype info and insert units before it", + "Look for comma before dtype", + "+2 to skip \", \"", + "No comma found, insert at start of dtype" + ], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_get_default_backend", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 91, - "signature": "()", + "name": "__str__", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1261, + "signature": "(self)", "parameters": [], "returns": null, - "existing_docstring": "DEPRECATED: Use _get_pint_helper() instead.", + "existing_docstring": "String representation for printing.", "harvested_comments": [], "status": "minimal", "needs": [ "NEEDS_PARAMETERS" ], - "parent_class": null, + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_extract_units_info", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 96, - "signature": "(obj)", + "name": "__array_function__", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_array.py", + "line": 1353, + "signature": "(self, func, types, args, kwargs)", "parameters": [ { - "name": "obj", + "name": "func", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "types", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "args", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "kwargs", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Extract units information from various object types.\n\nArgs:\n obj: Object that might have units (variable, quantity, expression, etc.)\n\nReturns:\n tuple: (has_units, units, backend) or (False, None, None)", + "existing_docstring": "Intercept numpy functions to preserve units.\n\nThis method is part of NumPy's __array_function__ protocol (NumPy 1.17+).\nIt allows UnitAwareArray to control how numpy functions behave when called\nwith UnitAwareArray instances.\n\nSupported Functions:\n-------------------\n- np.cross(): Cross product with unit multiplication\n- np.dot(): Dot product with unit multiplication\n- np.concatenate(): Concatenation with unit compatibility checking\n- np.stack(), np.vstack(), np.hstack(): Stacking with unit compatibility\n\nLimitations:\n-----------\n- np.array() and np.asarray() do NOT use __array_function__ protocol.\n They use the lower-level __array__() method instead, which returns\n plain numpy arrays. This is by design in NumPy.\n\n- Scalar indexing (arr[0]) returns plain Python scalars without units.\n This is expected behavior for ndarray subclasses.\n\n- For internal calculations, use raw arrays (e.g., mesh._points) instead\n of unit-aware arrays to avoid unit propagation issues.\n\nParameters\n----------\nfunc : callable\n NumPy function being called\ntypes : list\n Types of all arguments\nargs : tuple\n Positional arguments to func\nkwargs : dict\n Keyword arguments to func\n\nReturns\n-------\nresult\n Result with appropriate unit handling, or NotImplemented if the\n function is not handled (falls back to default numpy behavior)", "harvested_comments": [ - "PRIORITY 0: Check for SymPy expressions first (including derivatives)", - "This must come before unit-aware object check because UWexpression wraps", - "SymPy derivatives and we need to detect the derivative, not the wrapper's units", - "Check if object has a SymPy expression inside (UWexpression, variables, etc.)", - "TRANSPARENT CONTAINER PRINCIPLE (2025-11-26): Check .sym property (UWexpression," + "Supported numpy functions with unit handling", + "Register handlers for common numpy functions", + "Get units from the source array", + "Create the array using numpy's default behavior", + "Wrap with units if present" ], "status": "complete", "needs": [], - "parent_class": null, + "parent_class": "UnitAwareArray", "is_public": false }, { - "name": "_extract_units_from_sympy_expression", - "kind": "function", - "file": "src/underworld3/units.py", - "line": 342, - "signature": "(expr)", + "name": "__new__", + "kind": "method", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 28, + "signature": "(cls, name, index, system, pretty_str = None, latex_str = None, units = None)", "parameters": [ { - "name": "expr", + "name": "name", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "index", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "system", "type_hint": null, "default": null, "description": "" + }, + { + "name": "pretty_str", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "latex_str", + "type_hint": null, + "default": "None", + "description": "" + }, + { + "name": "units", + "type_hint": null, + "default": "None", + "description": "" } ], "returns": null, - "existing_docstring": "Extract units from SymPy expressions containing unit-aware variables.\n\nThis function analyzes mathematical expressions like 2*velocity to determine\ntheir units based on the unit-aware variables they contain.\n\nArgs:\n expr: SymPy expression\n\nReturns:\n tuple: (has_units, units, backend) or None if no units found", + "existing_docstring": "Create a new unit-aware coordinate symbol.\n\nParameters:\n name: Name of the coordinate (e.g., \"x\", \"y\", \"z\")\n index: Index of this coordinate (0, 1, or 2)\n system: The parent coordinate system\n pretty_str: Pretty print string (optional)\n latex_str: LaTeX representation (optional)\n units: Units of this coordinate (optional)", "harvested_comments": [ - "Import the function extraction utilities", - "Extract all UW objects (function symbols) from the expression", - "Get the default model to access registered variables", - "Map function symbols back to their variables", - "Skip coordinate symbols" + "BaseScalar.__new__ doesn't take 'name' - it infers it from the system", + "Signature: BaseScalar.__new__(cls, index, system, pretty_str, latex_str)", + "Store units on the new object" + ], + "status": "partial", + "needs": [ + "NEEDS_RETURNS" + ], + "parent_class": "UnitAwareBaseScalar", + "is_public": false + }, + { + "name": "_patch_time_units", + "kind": "function", + "file": "src/underworld3/utilities/unit_aware_coordinates.py", + "line": 245, + "signature": "(mesh)", + "parameters": [ + { + "name": "mesh", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Patch mesh.t with time units from the model's scaling system.\n\nIf the model has a time scale defined, mesh.t gets those units.\nOtherwise mesh.t remains dimensionless (._units = None).", + "harvested_comments": [], + "status": "partial", + "needs": [ + "NEEDS_PARAMETERS", + "NEEDS_RETURNS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false }, { - "name": "_analyze_expression_units", + "name": "_collect_mesh_fields", "kind": "function", - "file": "src/underworld3/units.py", - "line": 410, - "signature": "(expr, unit_info_list)", + "file": "src/underworld3/utilities/uw_petsc_gen_xdmf.py", + "line": 447, + "signature": "(h5, numVertices, numCells)", "parameters": [ { - "name": "expr", + "name": "h5", "type_hint": null, "default": null, "description": "" }, { - "name": "unit_info_list", + "name": "numVertices", + "type_hint": null, + "default": null, + "description": "" + }, + { + "name": "numCells", "type_hint": null, "default": null, "description": "" } ], "returns": null, - "existing_docstring": "Analyze a SymPy expression to determine its resultant units.\n\nThis implements basic dimensional analysis for mathematical operations:\n- Addition/subtraction: units must be the same, result has same units\n- Multiplication: units multiply\n- Division: units divide\n- Powers: units are raised to the power\n\nArgs:\n expr: SymPy expression\n unit_info_list: List of (units, backend) tuples from variables in expression\n\nReturns:\n Units for the resulting expression", + "existing_docstring": "Collect mesh fields from HDF5 in either new (/fields) or legacy layouts.", "harvested_comments": [ - "For now, implement simple heuristics", - "TODO: Full dimensional analysis implementation", - "If expression is multiplication, combine units using Pint", - "Check if it's a constant times a variable", - "Simple case: constant * variable" + "New layout: /fields/, with /fields/coordinates reserved for point locations", + "Default to node-centered when ambiguous", + "Legacy layout" + ], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" + ], + "parent_class": null, + "is_public": false + }, + { + "name": "_vector_to_pv_vector", + "kind": "function", + "file": "src/underworld3/visualisation/visualisation.py", + "line": 206, + "signature": "(vector)", + "parameters": [ + { + "name": "vector", + "type_hint": null, + "default": null, + "description": "" + } + ], + "returns": null, + "existing_docstring": "Convert numpy coordinate array to pyvista compatible array", + "harvested_comments": [], + "status": "minimal", + "needs": [ + "NEEDS_PARAMETERS" ], - "status": "complete", - "needs": [], "parent_class": null, "is_public": false } diff --git a/docs/docstrings/review_queue.md b/docs/docstrings/review_queue.md index 7af96ff24..8add038fb 100644 --- a/docs/docstrings/review_queue.md +++ b/docs/docstrings/review_queue.md @@ -6,10 +6,10 @@ Generated by `docstring_sweep.py` | Status | Count | |--------|-------| -| none | 356 | -| minimal | 562 | -| partial | 413 | -| complete | 226 | +| none | 383 | +| minimal | 854 | +| partial | 863 | +| complete | 377 | ## By File @@ -17,152 +17,264 @@ Generated by `docstring_sweep.py` **Public API:** -- ❌ `SolverBaseClass` [class] L22 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `SNES_Scalar` [class] L807 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `SNES_Vector` [class] L1401 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `SNES_Stokes_SaddlePt` [class] L2092 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `u` [method] L74 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `u` [method] L78 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DuDt` [method] L95 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DuDt` [method] L99 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DFDt` [method] L105 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DFDt` [method] L109 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `E` [method] L115 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `L` [method] L119 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `W` [method] L123 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `Einv2` [method] L127 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `CoordinateSystem` [method] L131 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `get_snes_diagnostics` [method] L165 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `check_snes_convergence` [method] L248 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `solve_with_diagnostics` [method] L313 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `F0` [method] L598 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `F1` [method] L602 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `u` [method] L606 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `u` [method] L610 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DuDt` [method] L615 +- 📝 `SolverBaseClass` [class] L52 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `SNES_Scalar` [class] L2327 + - NEEDS_RETURNS +- 📝 `SNES_Vector` [class] L3263 + - NEEDS_RETURNS +- 📝 `SNES_MultiComponent` [class] L4334 + - NEEDS_RETURNS +- 📝 `SNES_Stokes_SaddlePt` [class] L4998 + - NEEDS_RETURNS +- ❌ `consistent_jacobian` [method] L185 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DuDt` [method] L619 +- ❌ `preconditioner` [method] L497 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DFDt` [method] L624 +- ❌ `is_setup` [method] L759 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `DFDt` [method] L628 +- ❌ `u` [method] L816 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `constitutive_model` [method] L634 +- ❌ `DuDt` [method] L838 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `constitutive_model` [method] L638 +- ❌ `DFDt` [method] L851 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `validate_solver` [method] L668 +- ❌ `F0` [method] L1811 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `get_dof_partition` [method] L687 +- ❌ `F1` [method] L1815 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `tolerance` [method] L908 +- ❌ `u` [method] L1830 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `tolerance` [method] L912 +- ❌ `DuDt` [method] L1845 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `solve` [method] L1264 +- ❌ `DFDt` [method] L1860 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `tolerance` [method] L1513 +- ❌ `constitutive_model` [method] L1881 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `tolerance` [method] L1516 +- ❌ `constant_nullspace` [method] L2502 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `solve` [method] L1931 +- ❌ `petsc_use_constant_nullspace` [method] L2571 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `p` [method] L2138 +- ❌ `tolerance` [method] L2597 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `p` [method] L2142 +- ❌ `tolerance` [method] L3440 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `tolerance` [method] L2335 +- ❌ `tolerance` [method] L4453 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `tolerance` [method] L2339 +- ❌ `tolerance` [method] L4457 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strategy` [method] L2351 +- ❌ `p` [method] L5102 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strategy` [method] L2355 +- ❌ `p` [method] L5106 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `PF0` [method] L2417 +- ❌ `tolerance` [method] L5792 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `PF0` [method] L2421 +- ❌ `strategy` [method] L5823 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `p` [method] L2427 +- ❌ `PF0` [method] L5911 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `p` [method] L2431 +- ❌ `p` [method] L5936 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `saddle_preconditioner` [method] L2436 +- ❌ `saddle_preconditioner` [method] L5956 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `saddle_preconditioner` [method] L2440 +- ❌ `petsc_use_nullspace` [method] L5974 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `validate_solver` [method] L2500 +- ❌ `petsc_use_pressure_nullspace` [method] L6025 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `get_dof_partition` [method] L2522 +- ❌ `multiplier_schur_pc` [method] L6066 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `solve` [method] L3092 +- ❌ `petsc_velocity_nullspace_basis` [method] L6121 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- 📝 `add_condition` [method] L405 +- 📝 `consistent_jacobian` [method] L142 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `set_custom_mg` [method] L294 + - NEEDS_RETURNS +- 📝 `set_custom_fmg` [method] L348 + - NEEDS_RETURNS +- 📝 `add_update_callback` [method] L383 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `preconditioner` [method] L471 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `is_setup` [method] L732 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `u` [method] L811 + - NEEDS_PARAMETERS +- ⚠️ `DuDt` [method] L833 + - NEEDS_PARAMETERS +- ⚠️ `DFDt` [method] L846 + - NEEDS_PARAMETERS +- ⚠️ `E` [method] L857 + - NEEDS_PARAMETERS +- ⚠️ `L` [method] L862 + - NEEDS_PARAMETERS +- ⚠️ `W` [method] L867 + - NEEDS_PARAMETERS +- ⚠️ `Einv2` [method] L872 + - NEEDS_PARAMETERS +- ⚠️ `CoordinateSystem` [method] L877 + - NEEDS_PARAMETERS +- 📝 `get_snes_diagnostics` [method] L913 + - NEEDS_PARAMETERS +- ✅ `check_snes_convergence` [method] L996 +- ✅ `solve_with_diagnostics` [method] L1061 +- 📝 `add_condition` [method] L1441 + - NEEDS_RETURNS +- ⚠️ `is_numeric_only` [method] L1574 + - NEEDS_PARAMETERS +- 📝 `add_essential_bc` [method] L1655 + - NEEDS_RETURNS +- 📝 `add_natural_bc` [method] L1698 + - NEEDS_RETURNS +- 📝 `add_dirichlet_bc` [method] L1756 + - NEEDS_RETURNS +- 📝 `u` [method] L1819 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `DuDt` [method] L1835 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `DFDt` [method] L1850 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `constitutive_model` [method] L1866 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `tau` [method] L1925 + - NEEDS_PARAMETERS +- ⚠️ `validate_solver` [method] L2076 + - NEEDS_PARAMETERS +- 📝 `get_dof_partition` [method] L2093 + - NEEDS_RETURNS +- 📝 `boundary_flux` [method] L2290 + - NEEDS_PARAMETERS +- 📝 `boundary_flux_field` [method] L2308 + - NEEDS_PARAMETERS +- 📝 `constant_nullspace` [method] L2485 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `petsc_use_constant_nullspace` [method] L2559 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `tolerance` [method] L2575 + - NEEDS_PARAMETERS +- ✅ `solve` [method] L3053 +- 📝 `tolerance` [method] L3423 + - NEEDS_PARAMETERS +- 📝 `add_nitsche_bc` [method] L3447 + - NEEDS_RETURNS +- ✅ `solve` [method] L4140 +- ⚠️ `n_components` [method] L4448 + - NEEDS_PARAMETERS +- 📝 `solve` [method] L4913 + - NEEDS_RETURNS +- 📝 `add_rotated_freeslip_bc` [method] L5338 + - NEEDS_RETURNS +- 📝 `boundary_normal_traction` [method] L5460 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `dynamic_topography` [method] L5479 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `add_nitsche_bc` [method] L5500 - NEEDS_RETURNS -- ⚠️ `is_numeric_only` [method] L538 +- 📝 `tolerance` [method] L5766 + - NEEDS_PARAMETERS +- 📝 `strategy` [method] L5804 + - NEEDS_PARAMETERS +- 📝 `PF0` [method] L5890 - NEEDS_PARAMETERS -- ⚠️ `add_essential_bc` [method] L576 +- 📝 `p` [method] L5917 - NEEDS_PARAMETERS -- ⚠️ `add_natural_bc` [method] L584 +- 📝 `saddle_preconditioner` [method] L5941 + - NEEDS_PARAMETERS +- 📝 `petsc_use_nullspace` [method] L5961 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `petsc_use_pressure_nullspace` [method] L5990 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `multiplier_schur_pc` [method] L6035 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `petsc_velocity_nullspace_basis` [method] L6071 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `validate_solver` [method] L6498 - NEEDS_PARAMETERS -- ⚠️ `add_dirichlet_bc` [method] L591 +- 📝 `get_dof_partition` [method] L6520 + - NEEDS_RETURNS +- 📝 `set_active_region` [method] L7605 + - NEEDS_RETURNS +- 📝 `compute_volume_residual_fields` [method] L7665 - NEEDS_PARAMETERS -- ⚠️ `get_local_field_is` [method] L3197 +- 📝 `compute_boundary_residual_fields` [method] L7804 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `get_local_field_is` [method] L7943 - NEEDS_PARAMETERS -- ⚠️ `estimate_dt` [method] L3267 +- ✅ `solve` [method] L8054 +- ⚠️ `estimate_dt` [method] L8348 - NEEDS_PARAMETERS
-Private/Internal (26 items) - -- `_Unknowns` [class] L53 - none -- `_Unknowns` [class] L2123 - none -- `__init__` [method] L32 - none -- `__init__` [method] L57 - none -- `__init__` [method] L832 - none -- `__init__` [method] L1427 - none -- `__init__` [method] L2126 - none -- `__init__` [method] L2149 - none -- `_object_viewer` [method] L133 - none -- `_reset` [method] L145 - none -- `_build` [method] L357 - none -- `_setup_problem_description` [method] L401 - none -- `_get_dof_partition_by_field_id` [method] L715 - none -- `_setup_discretisation` [method] L919 - none -- `_setup_pointwise_functions` [method] L1070 - none -- `_setup_solver` [method] L1206 - none -- `_object_viewer` [method] L1347 - none -- `_setup_pointwise_functions` [method] L1674 - none -- `_setup_solver` [method] L1819 - none -- `_object_viewer` [method] L2047 - none -- `_setup_history_terms` [method] L2300 - none -- `_object_viewer` [method] L2447 - none -- `_setup_pointwise_functions` [method] L2562 - none -- `_setup_solver` [method] L2934 - none -- `_setup_discretisation` [method] L1524 - minimal -- `_setup_discretisation` [method] L2773 - minimal +Private/Internal (69 items) + +- `_Unknowns` [class] L769 - partial +- `_Unknowns` [class] L5088 - minimal +- `__init__` [method] L62 - none +- `__init__` [method] L795 - none +- `__init__` [method] L2394 - none +- `__init__` [method] L3330 - none +- `__init__` [method] L4363 - none +- `__init__` [method] L5091 - none +- `__init__` [method] L5113 - none +- `_reset` [method] L893 - none +- `_build` [method] L1227 - none +- `_setup_problem_description` [method] L1437 - none +- `_setup_discretisation` [method] L2604 - none +- `_setup_pointwise_functions` [method] L2793 - none +- `_setup_solver` [method] L2951 - none +- `_setup_pointwise_functions` [method] L3766 - none +- `_setup_solver` [method] L4002 - none +- `_setup_discretisation` [method] L4464 - none +- `_setup_pointwise_functions` [method] L4590 - none +- `_setup_solver` [method] L4792 - none +- `_object_viewer` [method] L4981 - none +- `_setup_history_terms` [method] L5732 - none +- `_reset_stokes_nullspace` [method] L6141 - none +- `_setup_pointwise_functions` [method] L6559 - none +- `_setup_solver` [method] L7304 - none +- `_jacobian_unwrap` [function] L24 - partial +- `_jacobian_source` [method] L195 - partial +- `_newton_flux` [method] L236 - partial +- `_get_newton_alpha` [method] L266 - partial +- `_set_newton_alpha` [method] L280 - partial +- `_maybe_install_snes_update` [method] L407 - minimal +- `_scatter_global_to_fields` [method] L412 - partial +- `_gather_fields_to_global` [method] L441 - partial +- `_refresh_auxiliary_vec` [method] L449 - minimal +- `_dispatch_snes_update` [method] L455 - partial +- `_apply_preconditioner_options` [method] L509 - partial +- `_enforce_galerkin_for_geometric_mg` [method] L633 - partial +- `_check_expression_meshes` [method] L672 - partial +- `_object_viewer` [method] L881 - minimal +- `_warn_on_divergence` [method] L1119 - partial +- `_snes_solve_with_retries` [method] L1150 - complete +- `_continuation_solve` [method] L1196 - partial +- `_set_constants_on_ds` [method] L1393 - partial +- `_update_constants` [method] L1414 - partial +- `_value_first_bc_args` [method] L1610 - partial +- `_setup_tau_projection` [method] L1992 - minimal +- `_get_dof_partition_by_field_id` [method] L2122 - partial +- `_assemble_volume_reaction` [method] L2211 - partial +- `_attach_constant_nullspace` [method] L2505 - minimal +- `_object_viewer` [method] L3209 - minimal +- `_setup_discretisation` [method] L3616 - minimal +- `_object_viewer` [method] L4289 - minimal +- `_residual_is_nonlinear` [method] L5412 - partial +- `_pressure_dirichlet_bcs` [method] L6149 - minimal +- `_build_pressure_nullspace_vector` [method] L6158 - minimal +- `_build_velocity_nullspace_vector` [method] L6176 - minimal +- `_build_block_gauge_nullspace_vector` [method] L6211 - partial +- `_build_stokes_nullspace` [method] L6244 - minimal +- `_setup_block_fieldsplit_options` [method] L6287 - partial +- `_attach_stokes_nullspace` [method] L6322 - minimal +- `_build_velocity_rotation_nullspace` [method] L6368 - partial +- `_remove_velocity_rotation_gauge` [method] L6398 - partial +- `_object_viewer` [method] L6445 - minimal +- `_setup_discretisation` [method] L6911 - minimal +- `_constrain_interior_multipliers_in_section` [method] L7147 - partial +- `_setup_region_ds` [method] L7622 - partial +- `_ensure_local_field_index_sets` [method] L7929 - partial +- `_scatter_global_to_fields` [method] L7992 - partial +- `_gather_fields_to_global` [method] L8041 - partial
@@ -221,784 +333,1042 @@ Generated by `docstring_sweep.py` **Public API:** -- 📝 `SNES_Poisson` [class] L154 +- 📝 `SNES_Poisson` [class] L160 + - NEEDS_RETURNS +- ✅ `SNES_Darcy` [class] L360 +- 📝 `SNES_TransientDarcy` [class] L582 - NEEDS_RETURNS -- ✅ `SNES_Darcy` [class] L333 -- 📝 `SNES_Stokes` [class] L558 +- 📝 `SNES_Richards` [class] L861 - NEEDS_RETURNS -- 📝 `SNES_VE_Stokes` [class] L1119 +- 📝 `SNES_Stokes` [class] L1047 - NEEDS_RETURNS -- 📝 `SNES_Projection` [class] L1323 +- 📝 `SNES_VE_Stokes` [class] L1958 - NEEDS_RETURNS -- 📝 `SNES_Vector_Projection` [class] L1446 +- 📝 `SNES_Stokes_Constrained` [class] L2076 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `SNES_Projection` [class] L2554 + - NEEDS_RETURNS +- 📝 `SNES_Vector_Projection` [class] L2912 - NEEDS_RETURNS -- 📝 `SNES_Tensor_Projection` [class] L1591 +- 📝 `SNES_Tensor_Projection` [class] L3120 - NEEDS_RETURNS -- 📝 `SNES_AdvectionDiffusion` [class] L1739 +- 📝 `SNES_MultiComponent_Projection` [class] L3280 - NEEDS_RETURNS -- 📝 `SNES_Diffusion` [class] L2255 +- 📝 `SNES_AdvectionDiffusion` [class] L3458 - NEEDS_RETURNS -- 📝 `SNES_NavierStokes` [class] L2635 +- 📝 `SNES_Diffusion` [class] L4155 - NEEDS_RETURNS -- ⚠️ `poisson_problem_description` (SNES_Poisson) [method] L234 +- 📝 `SNES_NavierStokes` [class] L4544 + - NEEDS_RETURNS +- ❌ `storage` (SNES_TransientDarcy) [method] L690 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `delta_t` (SNES_TransientDarcy) [method] L700 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `DuDt` (SNES_TransientDarcy) [property] L710 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `DFDt` (SNES_TransientDarcy) [property] L714 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `water_content` (SNES_Richards) [method] L985 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `capacity` (SNES_Richards) [method] L999 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `tolerance` (SNES_Stokes_Constrained) [method] L2233 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `saddle_preconditioner` (SNES_Stokes_Constrained) [method] L2339 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `smoothing` (SNES_MultiComponent_Projection) [method] L3386 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `uw_weighting_function` (SNES_MultiComponent_Projection) [method] L3445 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `poisson_problem_description` (SNES_Poisson) [method] L261 + - NEEDS_PARAMETERS +- 📝 `f` (SNES_Poisson) [property] L273 + - NEEDS_PARAMETERS +- ⚠️ `f` (SNES_Poisson) [method] L293 + - NEEDS_PARAMETERS +- ⚠️ `CM_is_setup` (SNES_Poisson) [property] L355 - NEEDS_PARAMETERS -- 📝 `f` (SNES_Poisson) [property] L246 +- ⚠️ `darcy_problem_description` (SNES_Darcy) [method] L481 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_Poisson) [method] L266 +- ⚠️ `f` (SNES_Darcy) [property] L495 - NEEDS_PARAMETERS -- ⚠️ `CM_is_setup` (SNES_Poisson) [property] L328 +- ⚠️ `f` (SNES_Darcy) [method] L500 - NEEDS_PARAMETERS -- ⚠️ `darcy_problem_description` (SNES_Darcy) [method] L453 +- ⚠️ `darcy_flux` (SNES_Darcy) [property] L506 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_Darcy) [property] L467 +- ⚠️ `v` (SNES_Darcy) [property] L512 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_Darcy) [method] L472 +- ⚠️ `v` (SNES_Darcy) [method] L517 - NEEDS_PARAMETERS -- ⚠️ `darcy_flux` (SNES_Darcy) [property] L478 +- 📝 `solve` (SNES_Darcy) [method] L523 + - NEEDS_RETURNS +- ⚠️ `storage` (SNES_TransientDarcy) [property] L685 + - NEEDS_PARAMETERS +- ⚠️ `delta_t` (SNES_TransientDarcy) [property] L695 - NEEDS_PARAMETERS -- ⚠️ `v` (SNES_Darcy) [property] L484 +- ⚠️ `F0` (SNES_TransientDarcy) [property] L720 - NEEDS_PARAMETERS -- ⚠️ `v` (SNES_Darcy) [method] L489 +- ⚠️ `F1` (SNES_TransientDarcy) [property] L731 - NEEDS_PARAMETERS -- 📝 `solve` (SNES_Darcy) [method] L495 +- 📝 `estimate_dt` (SNES_TransientDarcy) [method] L744 + - NEEDS_PARAMETERS +- 📝 `solve` (SNES_TransientDarcy) [method] L793 - NEEDS_RETURNS -- ⚠️ `stokes_problem_description` (SNES_Stokes) [method] L733 +- 📝 `water_content` (SNES_Richards) [property] L969 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `capacity` (SNES_Richards) [property] L990 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `psi` (SNES_Richards) [property] L1004 - NEEDS_PARAMETERS -- ⚠️ `CM_is_setup` (SNES_Stokes) [property] L747 +- 📝 `F0` (SNES_Richards) [property] L1009 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `set_jacobian_F1_source` (SNES_Stokes) [method] L1183 + - NEEDS_RETURNS +- ✅ `solve` (SNES_Stokes) [method] L1261 +- 📝 `tau` (SNES_Stokes) [property] L1432 - NEEDS_PARAMETERS -- 📝 `strainrate` (SNES_Stokes) [property] L752 +- ⚠️ `stokes_problem_description` (SNES_Stokes) [method] L1491 - NEEDS_PARAMETERS -- 📝 `strainrate_1d` (SNES_Stokes) [property] L775 +- ⚠️ `CM_is_setup` (SNES_Stokes) [property] L1505 - NEEDS_PARAMETERS -- 📝 `strainrate_star_1d` (SNES_Stokes) [property] L794 +- 📝 `strainrate` (SNES_Stokes) [property] L1510 - NEEDS_PARAMETERS -- 📝 `stress_deviator` (SNES_Stokes) [property] L812 +- 📝 `strainrate_1d` (SNES_Stokes) [property] L1533 - NEEDS_PARAMETERS -- 📝 `stress_deviator_1d` (SNES_Stokes) [property] L837 +- 📝 `strainrate_star_1d` (SNES_Stokes) [property] L1552 - NEEDS_PARAMETERS -- 📝 `stress` (SNES_Stokes) [property] L852 +- 📝 `stress_deviator` (SNES_Stokes) [property] L1570 - NEEDS_PARAMETERS -- 📝 `stress_1d` (SNES_Stokes) [property] L875 +- 📝 `stress_deviator_1d` (SNES_Stokes) [property] L1595 - NEEDS_PARAMETERS -- 📝 `div_u` (SNES_Stokes) [property] L890 +- 📝 `stress` (SNES_Stokes) [property] L1610 - NEEDS_PARAMETERS -- 📝 `constraints` (SNES_Stokes) [property] L910 +- 📝 `stress_1d` (SNES_Stokes) [property] L1633 - NEEDS_PARAMETERS -- ⚠️ `constraints` (SNES_Stokes) [method] L925 +- 📝 `div_u` (SNES_Stokes) [property] L1648 - NEEDS_PARAMETERS -- 📝 `bodyforce` (SNES_Stokes) [property] L932 +- 📝 `constraints` (SNES_Stokes) [property] L1668 - NEEDS_PARAMETERS -- ⚠️ `bodyforce` (SNES_Stokes) [method] L952 +- ⚠️ `constraints` (SNES_Stokes) [method] L1683 - NEEDS_PARAMETERS -- 📝 `saddle_preconditioner` (SNES_Stokes) [property] L973 +- 📝 `bodyforce` (SNES_Stokes) [property] L1690 - NEEDS_PARAMETERS -- ⚠️ `saddle_preconditioner` (SNES_Stokes) [method] L993 +- ⚠️ `bodyforce` (SNES_Stokes) [method] L1710 - NEEDS_PARAMETERS -- 📝 `penalty` (SNES_Stokes) [property] L1000 +- ✅ `set_pressure_gauge` (SNES_Stokes) [method] L1738 +- 📝 `saddle_preconditioner` (SNES_Stokes) [property] L1776 - NEEDS_PARAMETERS -- ⚠️ `penalty` (SNES_Stokes) [method] L1026 +- ⚠️ `saddle_preconditioner` (SNES_Stokes) [method] L1803 - NEEDS_PARAMETERS -- 📝 `estimate_dt` (SNES_Stokes) [method] L1041 +- 📝 `penalty` (SNES_Stokes) [property] L1810 - NEEDS_PARAMETERS -- ⚠️ `delta_t` (SNES_VE_Stokes) [property] L1249 +- ⚠️ `penalty` (SNES_Stokes) [method] L1863 - NEEDS_PARAMETERS -- 📝 `solve` (SNES_VE_Stokes) [method] L1256 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `smoothing` (SNES_Projection) [property] L1414 +- 📝 `estimate_dt` (SNES_Stokes) [method] L1878 - NEEDS_PARAMETERS -- ⚠️ `smoothing` (SNES_Projection) [method] L1419 +- ⚠️ `delta_t` (SNES_VE_Stokes) [property] L2036 - NEEDS_PARAMETERS -- ⚠️ `uw_weighting_function` (SNES_Projection) [property] L1425 +- 📝 `tolerance` (SNES_Stokes_Constrained) [property] L2221 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `solve` (SNES_Stokes_Constrained) [method] L2241 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `saddle_preconditioner` (SNES_Stokes_Constrained) [property] L2325 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `add_constraint_bc` (SNES_Stokes_Constrained) [method] L2354 +- 📝 `multiplier` (SNES_Stokes_Constrained) [method] L2484 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `topography` (SNES_Stokes_Constrained) [method] L2496 +- ✅ `linear_solver` (SNES_Projection) [method] L2704 +- 📝 `smoothing` (SNES_Projection) [property] L2752 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `smoothing` (SNES_Projection) [method] L2790 + - NEEDS_PARAMETERS +- 📝 `smoothing_length` (SNES_Projection) [property] L2796 - NEEDS_PARAMETERS -- ⚠️ `uw_weighting_function` (SNES_Projection) [method] L1430 +- 📝 `smoothing_length` (SNES_Projection) [method] L2865 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `uw_weighting_function` (SNES_Projection) [property] L2891 - NEEDS_PARAMETERS -- ⚠️ `projection_problem_description` (SNES_Vector_Projection) [method] L1533 +- ⚠️ `uw_weighting_function` (SNES_Projection) [method] L2896 - NEEDS_PARAMETERS -- ⚠️ `smoothing` (SNES_Vector_Projection) [property] L1557 +- 📝 `smoothing` (SNES_Vector_Projection) [property] L3011 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `smoothing` (SNES_Vector_Projection) [method] L3027 - NEEDS_PARAMETERS -- ⚠️ `smoothing` (SNES_Vector_Projection) [method] L1562 +- 📝 `smoothing_length` (SNES_Vector_Projection) [property] L3033 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `smoothing_length` (SNES_Vector_Projection) [method] L3068 - NEEDS_PARAMETERS -- ⚠️ `penalty` (SNES_Vector_Projection) [property] L1568 +- 📝 `penalty` (SNES_Vector_Projection) [property] L3088 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `penalty` (SNES_Vector_Projection) [method] L3102 - NEEDS_PARAMETERS -- ⚠️ `penalty` (SNES_Vector_Projection) [method] L1573 +- ⚠️ `uw_weighting_function` (SNES_Vector_Projection) [property] L3109 - NEEDS_PARAMETERS -- ⚠️ `uw_weighting_function` (SNES_Vector_Projection) [property] L1580 +- ⚠️ `uw_weighting_function` (SNES_Vector_Projection) [method] L3114 - NEEDS_PARAMETERS -- ⚠️ `uw_weighting_function` (SNES_Vector_Projection) [method] L1585 +- 📝 `solve` (SNES_Tensor_Projection) [method] L3201 + - NEEDS_RETURNS +- ⚠️ `F0` (SNES_Tensor_Projection) [property] L3241 - NEEDS_PARAMETERS -- ⚠️ `solve` (SNES_Tensor_Projection) [method] L1660 +- ⚠️ `F1` (SNES_Tensor_Projection) [property] L3255 - NEEDS_PARAMETERS -- ⚠️ `F0` (SNES_Tensor_Projection) [property] L1692 +- ⚠️ `uw_scalar_function` (SNES_Tensor_Projection) [property] L3269 - NEEDS_PARAMETERS -- ⚠️ `F1` (SNES_Tensor_Projection) [property] L1706 +- ⚠️ `uw_scalar_function` (SNES_Tensor_Projection) [method] L3274 - NEEDS_PARAMETERS -- ⚠️ `uw_scalar_function` (SNES_Tensor_Projection) [property] L1720 +- 📝 `smoothing` (SNES_MultiComponent_Projection) [property] L3370 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `smoothing_length` (SNES_MultiComponent_Projection) [property] L3391 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `smoothing_length` (SNES_MultiComponent_Projection) [method] L3420 - NEEDS_PARAMETERS -- ⚠️ `uw_scalar_function` (SNES_Tensor_Projection) [method] L1725 +- ⚠️ `uw_weighting_function` (SNES_MultiComponent_Projection) [property] L3440 - NEEDS_PARAMETERS -- ⚠️ `F0` (SNES_AdvectionDiffusion) [property] L1906 +- ⚠️ `F0` (SNES_AdvectionDiffusion) [property] L3731 - NEEDS_PARAMETERS -- ⚠️ `F1` (SNES_AdvectionDiffusion) [property] L1920 +- ⚠️ `F1` (SNES_AdvectionDiffusion) [property] L3745 - NEEDS_PARAMETERS -- ⚠️ `adv_diff_slcn_problem_description` (SNES_AdvectionDiffusion) [method] L1933 +- ⚠️ `adv_diff_slcn_problem_description` (SNES_AdvectionDiffusion) [method] L3758 - NEEDS_PARAMETERS -- 📝 `f` (SNES_AdvectionDiffusion) [property] L1944 +- 📝 `f` (SNES_AdvectionDiffusion) [property] L3769 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_AdvectionDiffusion) [method] L1961 +- ⚠️ `f` (SNES_AdvectionDiffusion) [method] L3786 - NEEDS_PARAMETERS -- 📝 `V_fn` (SNES_AdvectionDiffusion) [property] L1967 +- 📝 `V_fn` (SNES_AdvectionDiffusion) [property] L3792 - NEEDS_PARAMETERS -- 📝 `V_fn` (SNES_AdvectionDiffusion) [method] L1981 +- 📝 `V_fn` (SNES_AdvectionDiffusion) [method] L3806 - NEEDS_RETURNS -- 📝 `delta_t` (SNES_AdvectionDiffusion) [property] L1997 - - NEEDS_PARAMETERS -- ⚠️ `delta_t` (SNES_AdvectionDiffusion) [method] L2021 +- 📝 `delta_t` (SNES_AdvectionDiffusion) [property] L3822 - NEEDS_PARAMETERS -- 📝 `estimate_dt` (SNES_AdvectionDiffusion) [method] L2066 +- ⚠️ `delta_t` (SNES_AdvectionDiffusion) [method] L3846 - NEEDS_PARAMETERS -- 📝 `solve` (SNES_AdvectionDiffusion) [method] L2198 +- ✅ `estimate_dt` (SNES_AdvectionDiffusion) [method] L3901 +- 📝 `solve` (SNES_AdvectionDiffusion) [method] L4093 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `F0` (SNES_Diffusion) [property] L2412 +- ⚠️ `F0` (SNES_Diffusion) [property] L4308 - NEEDS_PARAMETERS -- ⚠️ `F1` (SNES_Diffusion) [property] L2426 +- ⚠️ `F1` (SNES_Diffusion) [property] L4322 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_Diffusion) [property] L2440 +- ⚠️ `f` (SNES_Diffusion) [property] L4336 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_Diffusion) [method] L2445 +- ⚠️ `f` (SNES_Diffusion) [method] L4341 - NEEDS_PARAMETERS -- ⚠️ `delta_t` (SNES_Diffusion) [property] L2451 +- ⚠️ `delta_t` (SNES_Diffusion) [property] L4347 - NEEDS_PARAMETERS -- ⚠️ `delta_t` (SNES_Diffusion) [method] L2456 +- ⚠️ `delta_t` (SNES_Diffusion) [method] L4352 - NEEDS_PARAMETERS -- 📝 `estimate_dt` (SNES_Diffusion) [method] L2501 +- 📝 `estimate_dt` (SNES_Diffusion) [method] L4407 - NEEDS_PARAMETERS -- 📝 `solve` (SNES_Diffusion) [method] L2576 +- 📝 `solve` (SNES_Diffusion) [method] L4482 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `F0` (SNES_NavierStokes) [property] L2813 +- ⚠️ `F0` (SNES_NavierStokes) [property] L4713 - NEEDS_PARAMETERS -- ⚠️ `F1` (SNES_NavierStokes) [property] L2831 +- ⚠️ `F1` (SNES_NavierStokes) [property] L4728 - NEEDS_PARAMETERS -- ⚠️ `PF0` (SNES_NavierStokes) [property] L2862 +- ⚠️ `PF0` (SNES_NavierStokes) [property] L4759 - NEEDS_PARAMETERS -- ⚠️ `navier_stokes_problem_description` (SNES_NavierStokes) [method] L2877 +- ⚠️ `navier_stokes_problem_description` (SNES_NavierStokes) [method] L4774 - NEEDS_PARAMETERS -- ⚠️ `delta_t` (SNES_NavierStokes) [property] L2891 +- ⚠️ `delta_t` (SNES_NavierStokes) [property] L4788 - NEEDS_PARAMETERS -- ⚠️ `delta_t` (SNES_NavierStokes) [method] L2896 +- ⚠️ `delta_t` (SNES_NavierStokes) [method] L4793 - NEEDS_PARAMETERS -- ⚠️ `rho` (SNES_NavierStokes) [property] L2902 +- ⚠️ `rho` (SNES_NavierStokes) [property] L4799 - NEEDS_PARAMETERS -- ⚠️ `rho` (SNES_NavierStokes) [method] L2907 +- ⚠️ `rho` (SNES_NavierStokes) [method] L4804 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_NavierStokes) [property] L2913 +- ⚠️ `f` (SNES_NavierStokes) [property] L4810 - NEEDS_PARAMETERS -- ⚠️ `f` (SNES_NavierStokes) [method] L2918 +- ⚠️ `f` (SNES_NavierStokes) [method] L4815 - NEEDS_PARAMETERS -- ⚠️ `div_u` (SNES_NavierStokes) [property] L2924 +- ⚠️ `div_u` (SNES_NavierStokes) [property] L4821 - NEEDS_PARAMETERS -- 📝 `strainrate` (SNES_NavierStokes) [property] L2931 +- 📝 `strainrate` (SNES_NavierStokes) [property] L4828 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `DuDt` (SNES_NavierStokes) [property] L2941 +- ⚠️ `DuDt` (SNES_NavierStokes) [property] L4838 - NEEDS_PARAMETERS -- ⚠️ `DuDt` (SNES_NavierStokes) [method] L2946 +- ⚠️ `DuDt` (SNES_NavierStokes) [method] L4843 - NEEDS_PARAMETERS -- ⚠️ `DFDt` (SNES_NavierStokes) [property] L2955 +- ⚠️ `DFDt` (SNES_NavierStokes) [property] L4852 - NEEDS_PARAMETERS -- ⚠️ `constraints` (SNES_NavierStokes) [property] L2960 +- ⚠️ `constraints` (SNES_NavierStokes) [property] L4857 - NEEDS_PARAMETERS -- ⚠️ `constraints` (SNES_NavierStokes) [method] L2965 +- ⚠️ `constraints` (SNES_NavierStokes) [method] L4862 - NEEDS_PARAMETERS -- ⚠️ `bodyforce` (SNES_NavierStokes) [property] L2972 +- ⚠️ `bodyforce` (SNES_NavierStokes) [property] L4869 - NEEDS_PARAMETERS -- ⚠️ `bodyforce` (SNES_NavierStokes) [method] L2977 +- ⚠️ `bodyforce` (SNES_NavierStokes) [method] L4874 - NEEDS_PARAMETERS -- ⚠️ `saddle_preconditioner` (SNES_NavierStokes) [property] L2983 +- ⚠️ `saddle_preconditioner` (SNES_NavierStokes) [property] L4880 - NEEDS_PARAMETERS -- ⚠️ `saddle_preconditioner` (SNES_NavierStokes) [method] L2988 +- ⚠️ `saddle_preconditioner` (SNES_NavierStokes) [method] L4885 - NEEDS_PARAMETERS -- ⚠️ `penalty` (SNES_NavierStokes) [property] L2995 +- ⚠️ `penalty` (SNES_NavierStokes) [property] L4892 - NEEDS_PARAMETERS -- ⚠️ `penalty` (SNES_NavierStokes) [method] L3000 +- ⚠️ `penalty` (SNES_NavierStokes) [method] L4897 - NEEDS_PARAMETERS -- 📝 `solve` (SNES_NavierStokes) [method] L3006 +- 📝 `solve` (SNES_NavierStokes) [method] L4904 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `estimate_dt` (SNES_NavierStokes) [method] L3072 +- 📝 `estimate_dt` (SNES_NavierStokes) [method] L4981 - NEEDS_PARAMETERS
-Private/Internal (14 items) +Private/Internal (24 items) + +- `_BlockConstraintBC` [class] L2041 - partial +- `__init__` [method] L199 - none +- `__init__` [method] L415 - none +- `__init__` [method] L634 - none +- `__init__` [method] L950 - none +- `__init__` [method] L1133 - none +- `__init__` [method] L1996 - none +- `__init__` [method] L2055 - none +- `__init__` [method] L2125 - none +- `__init__` [method] L2653 - none +- `__init__` [method] L2968 - none +- `__init__` [method] L3179 - none +- `__init__` [method] L3330 - none +- `__init__` [method] L3612 - none +- `__init__` [method] L4210 - none +- `__init__` [method] L4622 - none +- `_object_viewer` [method] L3601 - none +- `_object_viewer` [method] L4200 - none +- `_object_viewer` [method] L4610 - none +- `_apply_unit_aware_scaling` [function] L79 - complete +- `_create_stress_history_ddt` [method] L1217 - partial +- `_warn_if_monolithic_direct` [method] L2252 - partial +- `_maybe_install_auto_gauge` [method] L2283 - partial +- `_viscosity_scale` [method] L2346 - minimal + +
+ +### `src/underworld3/cython/petsc_types.pyx` + +**Public API:** + +- ❌ `PtrContainer` [class] L3 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `allocate` [method] L24 + - NEEDS_PARAMETERS +- ⚠️ `copy_residual_from` [method] L40 + - NEEDS_PARAMETERS +- ⚠️ `copy_bcs_from` [method] L44 + - NEEDS_PARAMETERS +- ⚠️ `copy_jacobian_from` [method] L48 + - NEEDS_PARAMETERS +- ⚠️ `copy_bd_residual_from` [method] L52 + - NEEDS_PARAMETERS +- ⚠️ `copy_bd_jacobian_from` [method] L56 + - NEEDS_PARAMETERS + +
+Private/Internal (2 items) -- `__init__` [method] L194 - none -- `__init__` [method] L398 - none -- `__init__` [method] L661 - none -- `__init__` [method] L1203 - none -- `__init__` [method] L1374 - none -- `__init__` [method] L1493 - none -- `__init__` [method] L1638 - none -- `__init__` [method] L1815 - none -- `__init__` [method] L2317 - none -- `__init__` [method] L2724 - none -- `_object_viewer` [method] L1804 - none -- `_object_viewer` [method] L2307 - none -- `_object_viewer` [method] L2712 - none -- `_apply_unit_aware_scaling` [function] L73 - complete +- `__cinit__` [method] L5 - none +- `__dealloc__` [method] L12 - none
-### `src/underworld3/ckdtree.pyx` +### `src/underworld3/function/analytic.pyx` **Public API:** -- ❌ `KDTree` [class] L17 +- ❌ `AnalyticSolNL_base` [class] L52 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `AnalyticSolNL_velocity_x` [class] L56 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `AnalyticSolNL_velocity_y` [class] L61 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `AnalyticSolNL_velocity` [class] L66 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `AnalyticSolNL_bodyforce_x` [class] L74 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `AnalyticSolNL_bodyforce_y` [class] L79 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `AnalyticSolNL_bodyforce` [class] L84 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `n` [method] L86 +- ❌ `AnalyticSolNL_viscosity` [class] L92 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `ndim` [method] L90 +- ❌ `AnalyticSolCx_base` [class] L105 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `kdtree_points` [method] L160 +- ❌ `AnalyticSolCx_velocity_x` [class] L109 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `find_closest_point` [method] L170 +- ❌ `AnalyticSolCx_velocity_y` [class] L114 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `find_closest_n_points` [method] L215 +- ❌ `AnalyticSolCx_velocity` [class] L119 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `query` [method] L273 +- ❌ `AnalyticSolCx_pressure` [class] L127 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `rbf_interpolator_local` [method] L336 +- ❌ `AnalyticSolCx_viscosity` [class] L133 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `old_rbf_interpolator_local_from_kdtree` [method] L348 +- ❌ `AnalyticSolCx_stress_xx` [class] L142 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `old_rbf_interpolator_local_to_kdtree` [method] L409 +- ❌ `AnalyticSolCx_stress_yy` [class] L147 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `rbf_interpolator_local_from_kdtree` [method] L476 +- ❌ `AnalyticSolCx_stress_xy` [class] L152 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ⚠️ `build_index` [method] L155 +- 📝 `sympy_function_printable` [class] L30 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `SolCx` [class] L159 + - NEEDS_RETURNS +- ❌ `eval` [method] L69 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `eval` [method] L87 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `eval` [method] L122 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `evaluate_velocity` [method] L211 + - NEEDS_PARAMETERS +- ⚠️ `velocity_error` [method] L222 + - NEEDS_PARAMETERS +- 📝 `evaluate_stress` [method] L228 + - NEEDS_PARAMETERS +- ⚠️ `topography_top` [method] L245 - NEEDS_PARAMETERS
-Private/Internal (3 items) +Private/Internal (14 items) -- `__cinit__` [method] L59 - none -- `__dealloc__` [method] L81 - none -- `_convert_coords_to_tree_units` [method] L92 - none +- `__init__` [method] L183 - none +- `_ccode` [method] L38 - none +- `_eval_evalf` [method] L58 - none +- `_eval_evalf` [method] L63 - none +- `_eval_evalf` [method] L76 - none +- `_eval_evalf` [method] L81 - none +- `_eval_evalf` [method] L94 - none +- `_eval_evalf` [method] L111 - none +- `_eval_evalf` [method] L116 - none +- `_eval_evalf` [method] L129 - none +- `_eval_evalf` [method] L135 - none +- `_eval_evalf` [method] L144 - none +- `_eval_evalf` [method] L149 - none +- `_eval_evalf` [method] L154 - none
-### `src/underworld3/cython/petsc_maths.pyx` +### `src/underworld3/scaling/_utils.py` **Public API:** -- ❌ `Integral` [class] L16 +- ❌ `TransformedDict` [class] L24 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `ensure_to_base_units` [function] L20 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `CellWiseIntegral` [class] L208 +- ❌ `get` (TransformedDict) [method] L48 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `has_meaningful_units` [method] L131 +- ❌ `setdefault` (TransformedDict) [method] L51 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `evaluate` [method] L247 +- ❌ `pop` (TransformedDict) [method] L54 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- 📝 `evaluate` [method] L55 +- ❌ `update` (TransformedDict) [method] L59 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `copy` (TransformedDict) [method] L65 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `fromkeys` (TransformedDict) [method] L69 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `ensure_lower` [function] L15 - NEEDS_PARAMETERS
-Private/Internal (2 items) +Private/Internal (7 items) -- `__init__` [method] L46 - none -- `__init__` [method] L238 - none +- `__init__` [method] L36 - none +- `_process_args` [method] L28 - none +- `__getitem__` [method] L39 - none +- `__setitem__` [method] L42 - none +- `__delitem__` [method] L45 - none +- `__contains__` [method] L62 - none +- `_repr_html_` [method] L72 - none
-### `src/underworld3/cython/petsc_types.pyx` +### `src/underworld3/utilities/_api_tools.py` **Public API:** -- ❌ `PtrContainer` [class] L1 +- ❌ `class_or_instance_method` [class] L22 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- 📝 `Stateful` [class] L1 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `SymbolicProperty` [class] L38 + - NEEDS_RETURNS +- ✅ `ExpressionDescriptor` [class] L132 +- 📝 `Parameter` [class] L334 + - NEEDS_RETURNS +- 📝 `Template` [class] L369 + - NEEDS_RETURNS +- 📝 `uw_object` [class] L442 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ❌ `view` (uw_object) [method] L512 - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `uw_object_counter` (uw_object) [method] L460 + - NEEDS_PARAMETERS +- ⚠️ `instance_number` (uw_object) [property] L465 + - NEEDS_PARAMETERS -### `src/underworld3/function/_dminterp_wrapper.pyx` +
+Private/Internal (22 items) + +- `__init__` [method] L10 - none +- `__init__` [method] L23 - none +- `__init__` [method] L81 - none +- `__init__` [method] L198 - none +- `__init__` [method] L356 - none +- `__init__` [method] L402 - none +- `__init__` [method] L451 - none +- `_increment` [method] L14 - none +- `_get_state` [method] L17 - none +- `__get__` [method] L26 - none +- `__str__` [method] L469 - none +- `_ipython_display_` [method] L479 - none +- `_object_viewer` [method] L546 - none +- `__set_name__` [method] L87 - minimal +- `__get__` [method] L92 - minimal +- `__set__` [method] L98 - minimal +- `__delete__` [method] L124 - minimal +- `__set_name__` [method] L220 - minimal +- `__get__` [method] L227 - minimal +- `__set__` [method] L269 - partial +- `__get__` [method] L405 - partial +- `_reset` [method] L474 - minimal + +
+ +### `src/underworld3/utilities/read_medit_ascii.py` **Public API:** -- ❌ `CachedDMInterpolationInfo` [class] L39 +- ❌ `ReadError` [class] L14 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `create_structure` [method] L78 +- ❌ `WriteError` [class] L18 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `evaluate` [method] L144 +- ❌ `CorruptionError` [class] L22 - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `read_ascii_buffer` [function] L109 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `is_buffer` [function] L29 + - NEEDS_PARAMETERS +- ⚠️ `open_file` [function] L38 + - NEEDS_PARAMETERS +- ⚠️ `read_medit_ascii` [function] L47 + - NEEDS_PARAMETERS +- ⚠️ `print_medit_mesh_info` [function] L57 + - NEEDS_PARAMETERS -
-Private/Internal (3 items) - -- `__cinit__` [method] L71 - none -- `__dealloc__` [method] L191 - none -- `__repr__` [method] L203 - none - -
- -### `src/underworld3/function/_function.pyx` +### `src/underworld3/utilities/uw_petsc_gen_xdmf.py` **Public API:** -- ❌ `UnderworldAppliedFunction` [class] L50 +- ❌ `Xdmf` [class] L8 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `UnderworldAppliedFunctionDeriv` [class] L84 +- ❌ `writeHeader` (Xdmf) [method] L48 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `UnderworldFunction` [class] L92 +- ❌ `writeCells` (Xdmf) [method] L61 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `fdiff` [method] L90 +- ❌ `writeVertices` (Xdmf) [method] L76 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `global_evaluate_nd` [method] L288 +- ❌ `writeLocations` (Xdmf) [method] L90 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `evaluate_nd` [method] L662 +- ❌ `writeTimeGridHeader` (Xdmf) [method] L103 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_interpolate` [method] L838 +- ❌ `writeHybridSpaceGridHeader` (Xdmf) [method] L122 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `interpolate_vars_on_mesh` [method] L977 +- ❌ `writeSpaceGridHeader` (Xdmf) [method] L126 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `rbf_evaluate` [method] L1119 +- ❌ `writeFieldSingle` (Xdmf) [method] L152 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `dm_swarm_get_migrate_type` [method] L1241 +- ❌ `writeFieldComponents` (Xdmf) [method] L214 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `dm_swarm_set_migrate_type` [method] L1253 +- ❌ `writeField` (Xdmf) [method] L290 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `writeSpaceGridFooter` (Xdmf) [method] L323 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `writeParticleGridHeader` (Xdmf) [method] L327 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `writeParticleField` (Xdmf) [method] L340 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `writeTimeGridFooter` (Xdmf) [method] L355 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `writeFooter` (Xdmf) [method] L359 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `write` (Xdmf) [method] L363 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `generateXdmf` [function] L481 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `generate_uw_Xdmf` [function] L556 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- 📝 `fdiff` [method] L56 - - NEEDS_PARAMETERS, NEEDS_RETURNS
-Private/Internal (8 items) +Private/Internal (2 items) -- `_latex` [method] L66 - none -- `__new__` [method] L131 - none -- `_collect_mesh_varfns` [method] L182 - none -- `_lambdify_and_evaluate` [method] L203 - none -- `_project_to_work_variable` [method] L458 - none -- `_clement_to_work_variable` [method] L539 - none -- `_dmswarm_get_migrate_type` [method] L1265 - none -- `_dmswarm_set_migrate_type` [method] L1275 - none +- `__init__` [method] L9 - none +- `_collect_mesh_fields` [function] L447 - minimal
-### `src/underworld3/function/analytic.pyx` +### `src/underworld3/_var_types.py` + +**Public API:** + +- 📝 `VarType` [class] L21 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +### `src/underworld3/checkpoint/backend.py` **Public API:** -- ❌ `sympy_function_printable` [class] L18 +- 📝 `CheckpointBackend` [class] L18 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `InMemoryBackend` [class] L43 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ❌ `save_vector` (CheckpointBackend) [method] L30 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `load_vector` (CheckpointBackend) [method] L32 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_base` [class] L40 +- ❌ `save_metadata` (CheckpointBackend) [method] L34 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_velocity_x` [class] L44 +- ❌ `load_metadata` (CheckpointBackend) [method] L36 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_velocity_y` [class] L50 +- ❌ `list_vectors` (CheckpointBackend) [method] L38 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_velocity` [class] L55 +- ❌ `list_metadata` (CheckpointBackend) [method] L40 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_bodyforce_x` [class] L62 +- ❌ `save_vector` (InMemoryBackend) [method] L56 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_bodyforce_y` [class] L68 +- ❌ `load_vector` (InMemoryBackend) [method] L61 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_bodyforce` [class] L73 +- ❌ `save_metadata` (InMemoryBackend) [method] L66 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `AnalyticSolNL_viscosity` [class] L80 +- ❌ `load_metadata` (InMemoryBackend) [method] L71 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `eval` [method] L58 +- ❌ `list_vectors` (InMemoryBackend) [method] L76 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `eval` [method] L76 +- ❌ `list_metadata` (InMemoryBackend) [method] L79 - NEEDS_OVERVIEW, NEEDS_PARAMETERS
-Private/Internal (6 items) +Private/Internal (1 items) -- `_ccode` [method] L27 - none -- `_eval_evalf` [method] L47 - none -- `_eval_evalf` [method] L52 - none -- `_eval_evalf` [method] L65 - none -- `_eval_evalf` [method] L70 - none -- `_eval_evalf` [method] L83 - none +- `__init__` [method] L52 - none
-### `src/underworld3/_var_types.py` +### `src/underworld3/checkpoint/snapshot.py` **Public API:** -- 📝 `VarType` [class] L21 +- 📝 `SnapshotInvalidatedError` [class] L52 - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `Snapshot` [class] L80 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `snapshot` [function] L171 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `restore` [function] L268 + - NEEDS_RETURNS -### `src/underworld3/constitutive_models.py` +
+Private/Internal (12 items) + +- `_mesh_coords_key` [function] L142 - none +- `_meshvar_key` [function] L146 - none +- `_swarm_coords_key` [function] L150 - none +- `_swarmvar_key` [function] L154 - none +- `_capture_mesh` [function] L200 - none +- `_capture_swarm` [function] L246 - none +- `_build_mesh_payload` [function] L356 - none +- `_build_swarm_payload` [function] L374 - none +- `_swarm_stable_name` [function] L74 - minimal +- `_is_internal_swarmvar` [function] L158 - partial +- `_state_bearer_key` [function] L220 - minimal +- `_capture_state_bearer` [function] L226 - partial + +
+ +### `src/underworld3/checkpoint/state.py` **Public API:** -- 📝 `Constitutive_Model` [class] L122 - - NEEDS_RETURNS -- 📝 `ViscousFlowModel` [class] L494 - - NEEDS_RETURNS -- 📝 `ViscoPlasticFlowModel` [class] L725 - - NEEDS_RETURNS -- 📝 `ViscoElasticPlasticFlowModel` [class] L945 +- 📝 `SnapshottableState` [class] L39 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `DiffusionModel` [class] L1537 - - NEEDS_RETURNS -- 📝 `AnisotropicDiffusionModel` [class] L1659 +- 📝 `Snapshottable` [class] L54 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `GenericFluxModel` [class] L1727 +- ❌ `state` (Snapshottable) [property] L77 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS + +### `src/underworld3/checkpoint/tracker.py` + +**Public API:** + +- 📝 `TrackerState` [class] L41 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `DarcyFlowModel` [class] L1804 - - NEEDS_RETURNS -- 📝 `TransverseIsotropicFlowModel` [class] L1958 - - NEEDS_RETURNS -- 📝 `MultiMaterialConstitutiveModel` [class] L2158 +- 📝 `ModelTracker` [class] L53 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ✅ `validate_parameters` [function] L60 -- ✅ `create_unique_symbol` (Constitutive_Model) [method] L229 -- 📝 `Unknowns` (Constitutive_Model) [property] L275 - - NEEDS_PARAMETERS -- ⚠️ `Unknowns` (Constitutive_Model) [method] L288 - - NEEDS_PARAMETERS -- 📝 `K` (Constitutive_Model) [property] L295 - - NEEDS_PARAMETERS -- 📝 `u` (Constitutive_Model) [property] L306 - - NEEDS_PARAMETERS -- 📝 `grad_u` (Constitutive_Model) [property] L317 - - NEEDS_PARAMETERS -- 📝 `DuDt` (Constitutive_Model) [property] L333 - - NEEDS_PARAMETERS -- ⚠️ `DuDt` (Constitutive_Model) [method] L347 +- ❌ `state` (ModelTracker) [property] L124 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (ModelTracker) [method] L130 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `keys` (ModelTracker) [method] L113 - NEEDS_PARAMETERS -- ⚠️ `DFDt` (Constitutive_Model) [property] L357 + +
+Private/Internal (6 items) + +- `__init__` [method] L60 - none +- `__setattr__` [method] L70 - none +- `__getattr__` [method] L86 - none +- `__delattr__` [method] L100 - none +- `__contains__` [method] L110 - none +- `__repr__` [method] L117 - none + +
+ +### `src/underworld3/ckdtree.pyx` + +**Public API:** + +- 📝 `KDTree` [class] L37 + - NEEDS_RETURNS +- ⚠️ `live_count` [function] L28 - NEEDS_PARAMETERS -- ⚠️ `DFDt` (Constitutive_Model) [method] L363 +- ⚠️ `total_constructed` [function] L32 - NEEDS_PARAMETERS -- 📝 `C` (Constitutive_Model) [property] L375 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `c` (Constitutive_Model) [property] L394 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `flux` (Constitutive_Model) [property] L408 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `flux_1d` (Constitutive_Model) [property] L439 +- ⚠️ `n` [method] L118 - NEEDS_PARAMETERS -- ⚠️ `viscosity` (ViscousFlowModel) [property] L605 +- ⚠️ `ndim` [method] L123 - NEEDS_PARAMETERS -- ⚠️ `K` (ViscousFlowModel) [property] L612 +- ⚠️ `build_index` [method] L189 - NEEDS_PARAMETERS -- ⚠️ `flux` (ViscousFlowModel) [property] L617 +- ⚠️ `kdtree_points` [method] L195 - NEEDS_PARAMETERS -- 📝 `grad_u` (ViscousFlowModel) [property] L646 +- ✅ `find_closest_point` [method] L204 +- ✅ `find_closest_n_points` [method] L249 +- ✅ `query` [method] L307 +- ✅ `rbf_interpolator_local` [method] L372 +- 📝 `old_rbf_interpolator_local_from_kdtree` [method] L417 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `viscosity` (ViscoPlasticFlowModel) [property] L851 +- 📝 `old_rbf_interpolator_local_to_kdtree` [method] L478 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `plastic_correction` (ViscoPlasticFlowModel) [method] L901 - - NEEDS_PARAMETERS -- ⚠️ `ve_effective_viscosity` (_Parameters) [property] L1110 - - NEEDS_PARAMETERS -- ⚠️ `t_relax` (_Parameters) [property] L1150 +- ✅ `rbf_interpolator_local_from_kdtree` [method] L546 + +
+Private/Internal (3 items) + +- `__cinit__` [method] L86 - none +- `__dealloc__` [method] L112 - none +- `_convert_coords_to_tree_units` [method] L127 - complete + +
+ +### `src/underworld3/constitutive_models.py` + +**Public API:** + +- 📝 `Constitutive_Model` [class] L229 + - NEEDS_RETURNS +- 📝 `ViscousFlowModel` [class] L682 + - NEEDS_RETURNS +- 📝 `ViscoPlasticFlowModel` [class] L906 + - NEEDS_RETURNS +- 📝 `ViscoElasticPlasticFlowModel` [class] L1115 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `MaxwellExponentialFlowModel` [class] L2025 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `DiffusionModel` [class] L2049 + - NEEDS_RETURNS +- 📝 `AnisotropicDiffusionModel` [class] L2159 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `GenericFluxModel` [class] L2227 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `DarcyFlowModel` [class] L2304 +- 📝 `TransverseIsotropicFlowModel` [class] L2451 + - NEEDS_RETURNS +- 📝 `TransverseIsotropicVEPFlowModel` [class] L2651 + - NEEDS_RETURNS +- 📝 `TransverseIsotropicMaxwellExponentialFlowModel` [class] L3504 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `TransverseIsotropicVEPSplitFlowModel` [class] L3521 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `MultiMaterialConstitutiveModel` [class] L3905 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ❌ `viscosity` (_ViscousParameterAlias) [property] L158 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `viscosity` (_ViscousParameterAlias) [method] L162 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `yield_mode` (ViscoElasticPlasticFlowModel) [method] L1958 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `yield_softness` (ViscoElasticPlasticFlowModel) [method] L1986 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `dt_elastic` (_Parameters) [method] L2845 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `bdf_blend` (TransverseIsotropicVEPFlowModel) [method] L3094 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `yield_mode` (TransverseIsotropicVEPFlowModel) [method] L3465 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `yield_softness` (TransverseIsotropicVEPFlowModel) [method] L3485 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `tau_par_cap_factor` (TransverseIsotropicVEPSplitFlowModel) [method] L3622 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ✅ `validate_parameters` [function] L167 +- ✅ `create_unique_symbol` (Constitutive_Model) [method] L344 +- 📝 `Unknowns` (Constitutive_Model) [property] L390 + - NEEDS_PARAMETERS +- ⚠️ `Unknowns` (Constitutive_Model) [method] L403 + - NEEDS_PARAMETERS +- 📝 `K` (Constitutive_Model) [property] L410 + - NEEDS_PARAMETERS +- 📝 `u` (Constitutive_Model) [property] L421 - NEEDS_PARAMETERS -- ⚠️ `order` (ViscoElasticPlasticFlowModel) [property] L1161 +- 📝 `grad_u` (Constitutive_Model) [property] L432 - NEEDS_PARAMETERS -- ⚠️ `order` (ViscoElasticPlasticFlowModel) [method] L1166 +- 📝 `DuDt` (Constitutive_Model) [property] L448 - NEEDS_PARAMETERS -- ⚠️ `stress_star` (ViscoElasticPlasticFlowModel) [property] L1174 +- ⚠️ `DuDt` (Constitutive_Model) [method] L462 + - NEEDS_PARAMETERS +- ⚠️ `DFDt` (Constitutive_Model) [property] L472 + - NEEDS_PARAMETERS +- ⚠️ `DFDt` (Constitutive_Model) [method] L478 + - NEEDS_PARAMETERS +- 📝 `C` (Constitutive_Model) [property] L490 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `c` (Constitutive_Model) [property] L509 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `flux` (Constitutive_Model) [property] L523 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `flux_1d` (Constitutive_Model) [property] L554 - NEEDS_PARAMETERS -- ⚠️ `stress_2star` (ViscoElasticPlasticFlowModel) [property] L1182 +- 📝 `flux_jacobian` (Constitutive_Model) [property] L577 - NEEDS_PARAMETERS -- 📝 `E_eff` (ViscoElasticPlasticFlowModel) [property] L1196 +- 📝 `requires_stress_history` (Constitutive_Model) [property] L614 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `E_eff_inv_II` (ViscoElasticPlasticFlowModel) [property] L1227 +- 📝 `stress_history_ddt_kwargs` (Constitutive_Model) [property] L624 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `plastic_fraction` (Constitutive_Model) [property] L655 + - NEEDS_PARAMETERS +- ⚠️ `viscosity` (ViscousFlowModel) [property] L781 - NEEDS_PARAMETERS -- ⚠️ `K` (ViscoElasticPlasticFlowModel) [property] L1235 +- ⚠️ `K` (ViscousFlowModel) [property] L788 - NEEDS_PARAMETERS -- ⚠️ `viscosity` (ViscoElasticPlasticFlowModel) [property] L1240 +- ⚠️ `flux` (ViscousFlowModel) [property] L793 - NEEDS_PARAMETERS -- ⚠️ `plastic_correction` (ViscoElasticPlasticFlowModel) [method] L1318 +- 📝 `grad_u` (ViscousFlowModel) [property] L822 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `plastic_fraction` (ViscousFlowModel) [property] L838 - NEEDS_PARAMETERS -- 📝 `flux` (ViscoElasticPlasticFlowModel) [property] L1376 +- 📝 `viscosity` (ViscoPlasticFlowModel) [property] L1021 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `stress_projection` (ViscoElasticPlasticFlowModel) [method] L1392 +- 📝 `plastic_correction` (ViscoPlasticFlowModel) [method] L1071 - NEEDS_PARAMETERS -- ⚠️ `stress` (ViscoElasticPlasticFlowModel) [method] L1416 +- 📝 `dt_elastic` (_Parameters) [property] L1268 - NEEDS_PARAMETERS -- ⚠️ `is_elastic` (ViscoElasticPlasticFlowModel) [property] L1513 +- 📝 `dt_elastic` (_Parameters) [method] L1278 + - NEEDS_RETURNS +- ⚠️ `ve_effective_viscosity` (_Parameters) [property] L1349 - NEEDS_PARAMETERS -- ⚠️ `is_viscoplastic` (ViscoElasticPlasticFlowModel) [property] L1526 +- ⚠️ `t_relax` (_Parameters) [property] L1372 - NEEDS_PARAMETERS -- ⚠️ `K` (DiffusionModel) [property] L1610 +- ⚠️ `order` (ViscoElasticPlasticFlowModel) [property] L1383 - NEEDS_PARAMETERS -- ⚠️ `diffusivity` (DiffusionModel) [property] L1615 +- 📝 `order` (ViscoElasticPlasticFlowModel) [method] L1388 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `effective_order` (ViscoElasticPlasticFlowModel) [property] L1419 - NEEDS_PARAMETERS -- ⚠️ `diffusivity` (_Parameters) [property] L1684 +- 📝 `stress_history_ddt_kwargs` (ViscoElasticPlasticFlowModel) [property] L1532 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `stress_star` (ViscoElasticPlasticFlowModel) [property] L1544 - NEEDS_PARAMETERS -- ⚠️ `diffusivity` (_Parameters) [method] L1689 +- ⚠️ `stress_2star` (ViscoElasticPlasticFlowModel) [property] L1552 - NEEDS_PARAMETERS -- ⚠️ `flux` (_Parameters) [property] L1759 +- 📝 `E_eff` (ViscoElasticPlasticFlowModel) [property] L1566 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `E_eff_inv_II` (ViscoElasticPlasticFlowModel) [property] L1624 - NEEDS_PARAMETERS -- ⚠️ `flux` (_Parameters) [method] L1764 +- ⚠️ `K` (ViscoElasticPlasticFlowModel) [property] L1632 - NEEDS_PARAMETERS -- ⚠️ `flux` (GenericFluxModel) [property] L1788 +- 📝 `viscosity` (ViscoElasticPlasticFlowModel) [property] L1651 - NEEDS_PARAMETERS -- ⚠️ `s` (_Parameters) [property] L1890 +- ⚠️ `plastic_correction` (ViscoElasticPlasticFlowModel) [method] L1756 - NEEDS_PARAMETERS -- ⚠️ `s` (_Parameters) [method] L1895 +- 📝 `flux` (ViscoElasticPlasticFlowModel) [property] L1814 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `stress_projection` (ViscoElasticPlasticFlowModel) [method] L1828 - NEEDS_PARAMETERS -- ⚠️ `K` (DarcyFlowModel) [property] L1904 +- 📝 `stress` (ViscoElasticPlasticFlowModel) [method] L1850 - NEEDS_PARAMETERS -- 📝 `flux` (DarcyFlowModel) [property] L1947 +- 📝 `yield_mode` (ViscoElasticPlasticFlowModel) [property] L1934 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `viscosity` (TransverseIsotropicFlowModel) [property] L2052 +- 📝 `yield_softness` (ViscoElasticPlasticFlowModel) [property] L1973 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `requires_stress_history` (ViscoElasticPlasticFlowModel) [property] L1991 + - NEEDS_PARAMETERS +- ⚠️ `plastic_fraction` (ViscoElasticPlasticFlowModel) [property] L1996 + - NEEDS_PARAMETERS +- ⚠️ `is_elastic` (ViscoElasticPlasticFlowModel) [property] L2001 + - NEEDS_PARAMETERS +- ⚠️ `is_viscoplastic` (ViscoElasticPlasticFlowModel) [property] L2014 + - NEEDS_PARAMETERS +- ⚠️ `K` (DiffusionModel) [property] L2110 + - NEEDS_PARAMETERS +- ⚠️ `diffusivity` (DiffusionModel) [property] L2115 - NEEDS_PARAMETERS -- ⚠️ `K` (TransverseIsotropicFlowModel) [property] L2059 +- ⚠️ `diffusivity` (_Parameters) [property] L2184 - NEEDS_PARAMETERS -- 📝 `grad_u` (TransverseIsotropicFlowModel) [property] L2066 +- ⚠️ `diffusivity` (_Parameters) [method] L2189 + - NEEDS_PARAMETERS +- ⚠️ `flux` (_Parameters) [property] L2259 + - NEEDS_PARAMETERS +- ⚠️ `flux` (_Parameters) [method] L2264 + - NEEDS_PARAMETERS +- ⚠️ `flux` (GenericFluxModel) [property] L2288 + - NEEDS_PARAMETERS +- ⚠️ `s` (_Parameters) [property] L2383 + - NEEDS_PARAMETERS +- ⚠️ `s` (_Parameters) [method] L2388 + - NEEDS_PARAMETERS +- ⚠️ `K` (DarcyFlowModel) [property] L2397 + - NEEDS_PARAMETERS +- 📝 `flux` (DarcyFlowModel) [property] L2440 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `flux` (MultiMaterialConstitutiveModel) [property] L2265 +- ⚠️ `viscosity` (TransverseIsotropicFlowModel) [property] L2545 + - NEEDS_PARAMETERS +- ⚠️ `K` (TransverseIsotropicFlowModel) [property] L2552 + - NEEDS_PARAMETERS +- 📝 `grad_u` (TransverseIsotropicFlowModel) [property] L2559 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `K` (MultiMaterialConstitutiveModel) [property] L2305 +- ⚠️ `dt_elastic` (_Parameters) [property] L2840 + - NEEDS_PARAMETERS +- ⚠️ `ve_effective_viscosity` (_Parameters) [property] L2887 + - NEEDS_PARAMETERS +- ⚠️ `t_relax` (_Parameters) [property] L2903 + - NEEDS_PARAMETERS +- ⚠️ `is_elastic` (TransverseIsotropicVEPFlowModel) [property] L2911 + - NEEDS_PARAMETERS +- ⚠️ `is_viscoplastic` (TransverseIsotropicVEPFlowModel) [property] L2918 + - NEEDS_PARAMETERS +- ⚠️ `order` (TransverseIsotropicVEPFlowModel) [property] L2925 + - NEEDS_PARAMETERS +- ⚠️ `order` (TransverseIsotropicVEPFlowModel) [method] L2930 + - NEEDS_PARAMETERS +- ⚠️ `effective_order` (TransverseIsotropicVEPFlowModel) [property] L2949 + - NEEDS_PARAMETERS +- ⚠️ `stress_history_ddt_kwargs` (TransverseIsotropicVEPFlowModel) [property] L3018 + - NEEDS_PARAMETERS +- 📝 `bdf_blend` (TransverseIsotropicVEPFlowModel) [property] L3074 - NEEDS_PARAMETERS, NEEDS_RETURNS - -
-Private/Internal (47 items) - -- `_Parameters` [class] L1666 - none -- `_Parameters` [class] L1742 - none -- `_Parameters` [class] L264 - partial -- `_Parameters` [class] L574 - partial -- `_Parameters` [class] L796 - partial -- `_Parameters` [class] L1012 - partial -- `_Parameters` [class] L1579 - partial -- `_Parameters` [class] L1850 - partial -- `_Parameters` [class] L2009 - partial -- `__init__` [method] L270 - none -- `__init__` [method] L559 - none -- `__init__` [method] L597 - none -- `__init__` [method] L776 - none -- `__init__` [method] L846 - none -- `__init__` [method] L967 - none -- `__init__` [method] L1074 - none -- `__init__` [method] L1602 - none -- `__init__` [method] L1667 - none -- `__init__` [method] L1743 - none -- `__init__` [method] L1872 - none -- `__init__` [method] L1994 - none -- `__init__` [method] L2042 - none -- `__init__` [method] L166 - partial -- `__init__` [method] L2176 - partial -- `_object_viewer` [method] L483 - none -- `_object_viewer` [method] L709 - none -- `_object_viewer` [method] L925 - none -- `_plastic_effective_viscosity` [property] L1279 - none -- `_object_viewer` [method] L1479 - none -- `_object_viewer` [method] L1645 - none -- `_object_viewer` [method] L1716 - none -- `_object_viewer` [method] L1794 - none -- `_object_viewer` [method] L1934 - none -- `_object_viewer` [method] L2142 - none -- `_object_viewer` [method] L2342 - none -- `_q` [method] L418 - minimal -- `_reset` [method] L461 - minimal -- `_build_c_tensor` [method] L475 - minimal -- `_q` [method] L622 - minimal -- `_build_c_tensor` [method] L661 - minimal -- `_build_c_tensor` [method] L1336 - minimal -- `_build_c_tensor` [method] L1619 - minimal -- `_build_c_tensor` [method] L1711 - minimal -- `_build_c_tensor` [method] L1908 - minimal -- `_build_c_tensor` [method] L2077 - minimal -- `_setup_shared_unknowns` [method] L2218 - minimal -- `_validate_model_compatibility` [method] L2236 - partial - -
- -### `src/underworld3/constitutive_models_new.py` - -**Public API:** - -- 📝 `Constitutive_Model` [class] L31 - - NEEDS_RETURNS -- 📝 `ViscousFlowModel` [class] L259 - - NEEDS_RETURNS -- 📝 `ViscoPlasticFlowModel` [class] L344 - - NEEDS_RETURNS -- 📝 `ViscoElasticPlasticFlowModel` [class] L786 - - NEEDS_RETURNS -- 📝 `DiffusionModel` [class] L1468 - - NEEDS_RETURNS -- 📝 `TransverseIsotropicFlowModel` [class] L1538 - - NEEDS_RETURNS -- 📝 `MultiMaterial` [class] L1686 +- ⚠️ `stress_star` (TransverseIsotropicVEPFlowModel) [property] L3100 + - NEEDS_PARAMETERS +- 📝 `E_eff` (TransverseIsotropicVEPFlowModel) [property] L3141 + - NEEDS_PARAMETERS +- ⚠️ `E_eff_inv_II` (TransverseIsotropicVEPFlowModel) [property] L3155 + - NEEDS_PARAMETERS +- 📝 `viscosity` (TransverseIsotropicVEPFlowModel) [property] L3162 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ❌ `k` (_Parameters) [property] L111 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `k` (_Parameters) [method] L115 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `solver` (Constitutive_Model) [method] L150 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `viscosity` (_Parameters) [property] L300 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `viscosity` (_Parameters) [method] L304 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_0` (_Parameters) [property] L434 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_0` (_Parameters) [method] L438 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_min` (_Parameters) [property] L443 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_min` (_Parameters) [method] L447 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_max` (_Parameters) [property] L452 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_max` (_Parameters) [method] L456 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress` (_Parameters) [property] L461 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress` (_Parameters) [method] L465 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress_min` (_Parameters) [property] L470 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress_min` (_Parameters) [method] L474 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II` (_Parameters) [property] L479 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II` (_Parameters) [method] L483 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II_min` (_Parameters) [property] L488 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II_min` (_Parameters) [method] L492 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `averaging_method` (_Parameters) [property] L497 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `averaging_method` (_Parameters) [method] L501 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `materialIndex` (_Parameters) [property] L507 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `materialIndex` (_Parameters) [method] L511 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `plastic_eff_viscosity` (_Parameters) [property] L519 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `viscosity` (_Parameters) [property] L607 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_0` (_Parameters) [property] L861 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_0` (_Parameters) [method] L865 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_modulus` (_Parameters) [property] L870 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_modulus` (_Parameters) [method] L874 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `dt_elastic` (_Parameters) [property] L879 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `dt_elastic` (_Parameters) [method] L883 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_min` (_Parameters) [property] L888 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_min` (_Parameters) [method] L892 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_max` (_Parameters) [property] L897 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `shear_viscosity_max` (_Parameters) [method] L901 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress` (_Parameters) [property] L906 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress` (_Parameters) [method] L910 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress_min` (_Parameters) [property] L915 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `yield_stress_min` (_Parameters) [method] L919 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II` (_Parameters) [property] L924 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II` (_Parameters) [method] L928 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `stress_star` (_Parameters) [property] L933 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `stress_star` (_Parameters) [method] L937 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `stress_star_star` (_Parameters) [property] L942 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `stress_star_star` (_Parameters) [method] L946 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II_min` (_Parameters) [property] L951 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `strainrate_inv_II_min` (_Parameters) [method] L955 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `averaging_method` (_Parameters) [property] L960 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `averaging_method` (_Parameters) [method] L964 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `materialIndex` (_Parameters) [property] L970 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `materialIndex` (_Parameters) [method] L974 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `t_relax` (_Parameters) [property] L981 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `ve_effective_viscosity` (_Parameters) [property] L997 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `plastic_eff_viscosity` (_Parameters) [property] L1091 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `viscosity` (_Parameters) [property] L1180 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `is_elastic` (ViscoElasticPlasticFlowModel) [property] L1440 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `is_viscoplastic` (ViscoElasticPlasticFlowModel) [property] L1455 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `diffusivity` (_Parameters) [property] L1502 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `diffusivity` (_Parameters) [method] L1506 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `eta_0` (_Parameters) [property] L1594 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `eta_0` (_Parameters) [method] L1598 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `eta_1` (_Parameters) [property] L1606 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `eta_1` (_Parameters) [method] L1610 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `director` (_Parameters) [property] L1618 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `director` (_Parameters) [method] L1622 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ⚠️ `solver` (Constitutive_Model) [property] L144 +- ⚠️ `K` (TransverseIsotropicVEPFlowModel) [property] L3194 + - NEEDS_PARAMETERS +- ⚠️ `flux` (TransverseIsotropicVEPFlowModel) [property] L3346 - NEEDS_PARAMETERS -- 📝 `C` (Constitutive_Model) [property] L159 +- 📝 `stress_projection` (TransverseIsotropicVEPFlowModel) [method] L3350 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `c` (Constitutive_Model) [property] L176 +- 📝 `stress` (TransverseIsotropicVEPFlowModel) [method] L3402 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `flux` (Constitutive_Model) [method] L184 +- 📝 `yield_mode` (TransverseIsotropicVEPFlowModel) [property] L3454 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `flux_1d` (Constitutive_Model) [method] L210 +- ⚠️ `yield_softness` (TransverseIsotropicVEPFlowModel) [property] L3480 + - NEEDS_PARAMETERS +- ⚠️ `requires_stress_history` (TransverseIsotropicVEPFlowModel) [property] L3490 + - NEEDS_PARAMETERS +- ⚠️ `plastic_fraction` (TransverseIsotropicVEPFlowModel) [property] L3495 - NEEDS_PARAMETERS -- 📝 `flux` (ViscoElasticPlasticFlowModel) [method] L1328 +- 📝 `tau_par_cap_factor` (TransverseIsotropicVEPSplitFlowModel) [property] L3602 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `stress` (TransverseIsotropicVEPSplitFlowModel) [method] L3792 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `flux` (MultiMaterialConstitutiveModel) [property] L4012 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `K` (MultiMaterialConstitutiveModel) [property] L4052 - NEEDS_PARAMETERS, NEEDS_RETURNS
-Private/Internal (31 items) - -- `_Parameters` [class] L100 - partial -- `_Parameters` [class] L284 - partial -- `_Parameters` [class] L392 - partial -- `_Parameters` [class] L813 - partial -- `_Parameters` [class] L1489 - partial -- `_Parameters` [class] L1574 - partial -- `__init__` [method] L66 - none -- `__init__` [method] L106 - none -- `__init__` [method] L290 - none -- `__init__` [method] L308 - none -- `__init__` [method] L387 - none -- `__init__` [method] L401 - none -- `__init__` [method] L819 - none -- `__init__` [method] L1293 - none -- `__init__` [method] L1495 - none -- `__init__` [method] L1510 - none -- `__init__` [method] L1580 - none -- `__init__` [method] L1629 - none -- `__init__` [method] L1694 - none -- `_reset` [method] L231 - none -- `_object_viewer` [method] L248 - none -- `_object_viewer` [method] L335 - none -- `_object_viewer` [method] L763 - none -- `_object_viewer` [method] L1390 - none -- `_object_viewer` [method] L1525 - none -- `_object_viewer` [method] L1670 - none -- `_build_c_tensor` [method] L240 - minimal -- `_build_c_tensor` [method] L314 - minimal -- `_build_c_tensor` [method] L1301 - minimal -- `_build_c_tensor` [method] L1516 - minimal -- `_build_c_tensor` [method] L1636 - minimal +Private/Internal (78 items) + +- `_Parameters` [class] L2166 - none +- `_Parameters` [class] L2242 - none +- `_ParameterBase` [class] L60 - partial +- `_ViscousParameterAlias` [class] L142 - partial +- `_Parameters` [class] L379 - partial +- `_Parameters` [class] L750 - partial +- `_Parameters` [class] L966 - partial +- `_Parameters` [class] L1240 - partial +- `_Parameters` [class] L2079 - partial +- `_Parameters` [class] L2343 - partial +- `_Parameters` [class] L2502 - partial +- `_Parameters` [class] L2808 - partial +- `__init__` [method] L385 - none +- `__init__` [method] L735 - none +- `__init__` [method] L773 - none +- `__init__` [method] L946 - none +- `__init__` [method] L1016 - none +- `__init__` [method] L1313 - none +- `__init__` [method] L2038 - none +- `__init__` [method] L2102 - none +- `__init__` [method] L2167 - none +- `__init__` [method] L2243 - none +- `__init__` [method] L2365 - none +- `__init__` [method] L2487 - none +- `__init__` [method] L2535 - none +- `__init__` [method] L2874 - none +- `__init__` [method] L3514 - none +- `__init__` [method] L3595 - none +- `__init__` [method] L273 - partial +- `__init__` [method] L1137 - partial +- `__init__` [method] L2676 - partial +- `__init__` [method] L3923 - partial +- `__setattr__` [method] L109 - none +- `_object_viewer` [method] L671 - none +- `_object_viewer` [method] L890 - none +- `_object_viewer` [method] L1095 - none +- `_plastic_effective_viscosity` [property] L1723 - none +- `_object_viewer` [method] L1900 - none +- `_object_viewer` [method] L2145 - none +- `_object_viewer` [method] L2216 - none +- `_object_viewer` [method] L2294 - none +- `_object_viewer` [method] L2427 - none +- `_object_viewer` [method] L2635 - none +- `_object_viewer` [method] L4089 - none +- `_list_valid_parameters` [method] L98 - minimal +- `_q` [method] L533 - minimal +- `_reset` [method] L596 - minimal +- `_update_history_coefficients` [method] L634 - partial +- `_update_history_post_solve` [method] L646 - partial +- `_build_c_tensor` [method] L663 - minimal +- `_q` [method] L798 - minimal +- `_build_c_tensor` [method] L842 - minimal +- `_update_bdf_coefficients` [method] L1436 - partial +- `_update_history_coefficients` [method] L1479 - partial +- `_update_history_post_solve` [method] L1520 - partial +- `_unclipped_ve_viscosity` [property] L1637 - partial +- `_build_c_tensor` [method] L1774 - minimal +- `_build_c_tensor` [method] L2119 - minimal +- `_build_c_tensor` [method] L2211 - minimal +- `_build_c_tensor` [method] L2401 - minimal +- `_build_c_tensor` [method] L2570 - minimal +- `_update_etd_coefficients` [method] L2956 - minimal +- `_update_history_coefficients` [method] L2980 - partial +- `_update_history_post_solve` [method] L3004 - minimal +- `_update_bdf_coefficients` [method] L3028 - minimal +- `_e_eff_for` [method] L3106 - partial +- `_plastic_effective_viscosity` [property] L3199 - partial +- `_eta_for_tensor` [method] L3240 - partial +- `_assemble_c_tensor` [method] L3285 - partial +- `_build_c_tensor` [method] L3318 - partial +- `_build_c_tensor_ve` [method] L3369 - minimal +- `_q_with` [method] L3438 - partial +- `_update_history_coefficients` [method] L3625 - partial +- `_eta_par_eff` [method] L3653 - partial +- `_eta_par_eff_lagged` [method] L3680 - partial +- `_build_split_c_tensors` [method] L3743 - partial +- `_setup_shared_unknowns` [method] L3965 - minimal +- `_validate_model_compatibility` [method] L3983 - partial
@@ -1009,11 +1379,11 @@ Generated by `docstring_sweep.py` - ✅ `UWCoordinate` [class] L51 - 📝 `CoordinateSystemType` [class] L299 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `GeographicCoordinateAccessor` [class] L491 +- 📝 `GeographicCoordinateAccessor` [class] L475 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `SphericalCoordinateAccessor` [class] L945 +- 📝 `SphericalCoordinateAccessor` [class] L925 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `CoordinateSystem` [class] L1353 +- 📝 `CoordinateSystem` [class] L1325 - NEEDS_PARAMETERS, NEEDS_RETURNS - 📝 `sym` (UWCoordinate) [property] L166 - NEEDS_PARAMETERS @@ -1026,162 +1396,162 @@ Generated by `docstring_sweep.py` - 📝 `units` (UWCoordinate) [property] L208 - NEEDS_PARAMETERS, NEEDS_RETURNS - ✅ `uwdiff` [function] L260 -- ✅ `geographic_to_cartesian` [function] L385 -- ✅ `cartesian_to_geographic` [function] L430 -- ⚠️ `lon` (GeographicCoordinateAccessor) [property] L585 +- ✅ `geographic_to_cartesian` [function] L369 +- ✅ `cartesian_to_geographic` [function] L414 +- ⚠️ `lon` (GeographicCoordinateAccessor) [property] L561 - NEEDS_PARAMETERS -- ⚠️ `lat` (GeographicCoordinateAccessor) [property] L591 +- ⚠️ `lat` (GeographicCoordinateAccessor) [property] L567 - NEEDS_PARAMETERS -- 📝 `depth` (GeographicCoordinateAccessor) [property] L597 +- 📝 `depth` (GeographicCoordinateAccessor) [property] L573 - NEEDS_PARAMETERS -- 📝 `coords` (GeographicCoordinateAccessor) [property] L624 +- 📝 `coords` (GeographicCoordinateAccessor) [property] L600 - NEEDS_PARAMETERS -- ⚠️ `unit_WE` (GeographicCoordinateAccessor) [property] L668 +- ⚠️ `unit_WE` (GeographicCoordinateAccessor) [property] L644 - NEEDS_PARAMETERS -- ⚠️ `unit_SN` (GeographicCoordinateAccessor) [property] L673 +- ⚠️ `unit_SN` (GeographicCoordinateAccessor) [property] L649 - NEEDS_PARAMETERS -- ⚠️ `unit_down` (GeographicCoordinateAccessor) [property] L678 +- ⚠️ `unit_down` (GeographicCoordinateAccessor) [property] L654 - NEEDS_PARAMETERS -- ⚠️ `unit_east` (GeographicCoordinateAccessor) [property] L685 +- ⚠️ `unit_east` (GeographicCoordinateAccessor) [property] L661 - NEEDS_PARAMETERS -- ⚠️ `unit_west` (GeographicCoordinateAccessor) [property] L690 +- ⚠️ `unit_west` (GeographicCoordinateAccessor) [property] L666 - NEEDS_PARAMETERS -- ⚠️ `unit_north` (GeographicCoordinateAccessor) [property] L695 +- ⚠️ `unit_north` (GeographicCoordinateAccessor) [property] L671 - NEEDS_PARAMETERS -- ⚠️ `unit_south` (GeographicCoordinateAccessor) [property] L700 +- ⚠️ `unit_south` (GeographicCoordinateAccessor) [property] L676 - NEEDS_PARAMETERS -- ⚠️ `unit_up` (GeographicCoordinateAccessor) [property] L705 +- ⚠️ `unit_up` (GeographicCoordinateAccessor) [property] L681 - NEEDS_PARAMETERS -- ⚠️ `unit_lon` (GeographicCoordinateAccessor) [property] L712 +- ⚠️ `unit_lon` (GeographicCoordinateAccessor) [property] L688 - NEEDS_PARAMETERS -- ⚠️ `unit_lat` (GeographicCoordinateAccessor) [property] L717 +- ⚠️ `unit_lat` (GeographicCoordinateAccessor) [property] L693 - NEEDS_PARAMETERS -- ⚠️ `unit_depth` (GeographicCoordinateAccessor) [property] L722 +- ⚠️ `unit_depth` (GeographicCoordinateAccessor) [property] L698 - NEEDS_PARAMETERS -- ✅ `to_cartesian` (GeographicCoordinateAccessor) [method] L728 -- ✅ `from_cartesian` (GeographicCoordinateAccessor) [method] L768 -- ✅ `points_to_cartesian` (GeographicCoordinateAccessor) [method] L808 -- ✅ `points_from_cartesian` (GeographicCoordinateAccessor) [method] L840 -- 📝 `view` (GeographicCoordinateAccessor) [method] L881 +- ✅ `to_cartesian` (GeographicCoordinateAccessor) [method] L704 +- ✅ `from_cartesian` (GeographicCoordinateAccessor) [method] L746 +- ✅ `points_to_cartesian` (GeographicCoordinateAccessor) [method] L788 +- ✅ `points_from_cartesian` (GeographicCoordinateAccessor) [method] L820 +- 📝 `view` (GeographicCoordinateAccessor) [method] L861 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `r` (SphericalCoordinateAccessor) [property] L1038 +- ⚠️ `r` (SphericalCoordinateAccessor) [property] L1010 - NEEDS_PARAMETERS -- ⚠️ `theta` (SphericalCoordinateAccessor) [property] L1044 +- ⚠️ `theta` (SphericalCoordinateAccessor) [property] L1016 - NEEDS_PARAMETERS -- ⚠️ `phi` (SphericalCoordinateAccessor) [property] L1050 +- ⚠️ `phi` (SphericalCoordinateAccessor) [property] L1022 - NEEDS_PARAMETERS -- 📝 `coords` (SphericalCoordinateAccessor) [property] L1061 +- 📝 `coords` (SphericalCoordinateAccessor) [property] L1033 - NEEDS_PARAMETERS -- ⚠️ `r_sym` (SphericalCoordinateAccessor) [property] L1121 +- ⚠️ `r_sym` (SphericalCoordinateAccessor) [property] L1093 - NEEDS_PARAMETERS -- ⚠️ `theta_sym` (SphericalCoordinateAccessor) [property] L1126 +- ⚠️ `theta_sym` (SphericalCoordinateAccessor) [property] L1098 - NEEDS_PARAMETERS -- ⚠️ `phi_sym` (SphericalCoordinateAccessor) [property] L1131 +- ⚠️ `phi_sym` (SphericalCoordinateAccessor) [property] L1103 - NEEDS_PARAMETERS -- ⚠️ `unit_r` (SphericalCoordinateAccessor) [property] L1143 +- ⚠️ `unit_r` (SphericalCoordinateAccessor) [property] L1115 - NEEDS_PARAMETERS -- ⚠️ `unit_theta` (SphericalCoordinateAccessor) [property] L1148 +- ⚠️ `unit_theta` (SphericalCoordinateAccessor) [property] L1120 - NEEDS_PARAMETERS -- ⚠️ `unit_phi` (SphericalCoordinateAccessor) [property] L1153 +- ⚠️ `unit_phi` (SphericalCoordinateAccessor) [property] L1125 - NEEDS_PARAMETERS -- ⚠️ `unit_radial` (SphericalCoordinateAccessor) [property] L1165 +- ⚠️ `unit_radial` (SphericalCoordinateAccessor) [property] L1137 - NEEDS_PARAMETERS -- ⚠️ `unit_outward` (SphericalCoordinateAccessor) [property] L1170 +- ⚠️ `unit_outward` (SphericalCoordinateAccessor) [property] L1142 - NEEDS_PARAMETERS -- ⚠️ `unit_inward` (SphericalCoordinateAccessor) [property] L1175 +- ⚠️ `unit_inward` (SphericalCoordinateAccessor) [property] L1147 - NEEDS_PARAMETERS -- ✅ `to_cartesian` (SphericalCoordinateAccessor) [method] L1181 -- ✅ `from_cartesian` (SphericalCoordinateAccessor) [method] L1209 -- ✅ `points_to_cartesian` (SphericalCoordinateAccessor) [method] L1241 -- ✅ `points_from_cartesian` (SphericalCoordinateAccessor) [method] L1261 -- 📝 `view` (SphericalCoordinateAccessor) [method] L1292 +- ✅ `to_cartesian` (SphericalCoordinateAccessor) [method] L1153 +- ✅ `from_cartesian` (SphericalCoordinateAccessor) [method] L1181 +- ✅ `points_to_cartesian` (SphericalCoordinateAccessor) [method] L1213 +- ✅ `points_from_cartesian` (SphericalCoordinateAccessor) [method] L1233 +- 📝 `view` (SphericalCoordinateAccessor) [method] L1264 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `coords` (CoordinateSystem) [property] L1874 +- 📝 `coords` (CoordinateSystem) [property] L1861 - NEEDS_PARAMETERS -- 📝 `units` (CoordinateSystem) [property] L1906 +- 📝 `units` (CoordinateSystem) [property] L1921 - NEEDS_PARAMETERS -- 📝 `with_units` (CoordinateSystem) [property] L1919 +- 📝 `with_units` (CoordinateSystem) [property] L1948 - NEEDS_PARAMETERS -- ⚠️ `shape` (CoordinateSystem) [property] L1941 +- ⚠️ `shape` (CoordinateSystem) [property] L1970 - NEEDS_PARAMETERS -- ⚠️ `is_symbol` (CoordinateSystem) [property] L1967 +- ⚠️ `is_symbol` (CoordinateSystem) [property] L1996 - NEEDS_PARAMETERS -- ⚠️ `is_Matrix` (CoordinateSystem) [property] L1972 +- ⚠️ `is_Matrix` (CoordinateSystem) [property] L2001 - NEEDS_PARAMETERS -- ⚠️ `is_scalar` (CoordinateSystem) [property] L1977 +- ⚠️ `is_scalar` (CoordinateSystem) [property] L2006 - NEEDS_PARAMETERS -- ⚠️ `is_number` (CoordinateSystem) [property] L1982 +- ⚠️ `is_number` (CoordinateSystem) [property] L2011 - NEEDS_PARAMETERS -- ⚠️ `is_commutative` (CoordinateSystem) [property] L1987 +- ⚠️ `is_commutative` (CoordinateSystem) [property] L2016 - NEEDS_PARAMETERS -- ⚠️ `X` (CoordinateSystem) [property] L2067 +- 📝 `X` (CoordinateSystem) [property] L2096 - NEEDS_PARAMETERS -- ⚠️ `x` (CoordinateSystem) [property] L2072 +- ⚠️ `x` (CoordinateSystem) [property] L2136 - NEEDS_PARAMETERS -- ⚠️ `N` (CoordinateSystem) [property] L2077 +- ⚠️ `N` (CoordinateSystem) [property] L2141 - NEEDS_PARAMETERS -- ⚠️ `R` (CoordinateSystem) [property] L2082 +- ⚠️ `R` (CoordinateSystem) [property] L2146 - NEEDS_PARAMETERS -- ⚠️ `r` (CoordinateSystem) [property] L2087 +- ⚠️ `r` (CoordinateSystem) [property] L2151 - NEEDS_PARAMETERS -- ⚠️ `xR` (CoordinateSystem) [property] L2092 +- ⚠️ `xR` (CoordinateSystem) [property] L2156 - NEEDS_PARAMETERS -- 📝 `geo` (CoordinateSystem) [property] L2097 +- 📝 `geo` (CoordinateSystem) [property] L2161 - NEEDS_PARAMETERS -- 📝 `spherical` (CoordinateSystem) [property] L2148 +- 📝 `spherical` (CoordinateSystem) [property] L2212 - NEEDS_PARAMETERS -- 📝 `rRotN` (CoordinateSystem) [property] L2200 +- 📝 `rRotN` (CoordinateSystem) [property] L2264 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `xRotN` (CoordinateSystem) [property] L2209 +- ⚠️ `xRotN` (CoordinateSystem) [property] L2273 - NEEDS_PARAMETERS -- 📝 `geoRotN` (CoordinateSystem) [property] L2214 +- 📝 `geoRotN` (CoordinateSystem) [property] L2278 - NEEDS_PARAMETERS -- ⚠️ `unit_e_0` (CoordinateSystem) [property] L2248 +- ⚠️ `unit_e_0` (CoordinateSystem) [property] L2312 - NEEDS_PARAMETERS -- ⚠️ `unit_e_1` (CoordinateSystem) [property] L2253 +- ⚠️ `unit_e_1` (CoordinateSystem) [property] L2317 - NEEDS_PARAMETERS -- ⚠️ `unit_e_2` (CoordinateSystem) [property] L2258 +- ⚠️ `unit_e_2` (CoordinateSystem) [property] L2322 - NEEDS_PARAMETERS -- ⚠️ `unit_i` (CoordinateSystem) [property] L2266 +- ⚠️ `unit_i` (CoordinateSystem) [property] L2330 - NEEDS_PARAMETERS -- ⚠️ `unit_j` (CoordinateSystem) [property] L2271 +- ⚠️ `unit_j` (CoordinateSystem) [property] L2335 - NEEDS_PARAMETERS -- ⚠️ `unit_k` (CoordinateSystem) [property] L2276 +- ⚠️ `unit_k` (CoordinateSystem) [property] L2340 - NEEDS_PARAMETERS -- ⚠️ `unit_ijk` (CoordinateSystem) [method] L2283 +- ⚠️ `unit_ijk` (CoordinateSystem) [method] L2347 - NEEDS_PARAMETERS -- ⚠️ `unit_vertical` (CoordinateSystem) [property] L2292 +- ⚠️ `unit_vertical` (CoordinateSystem) [property] L2356 - NEEDS_PARAMETERS -- ⚠️ `unit_horizontal` (CoordinateSystem) [property] L2312 +- ⚠️ `unit_horizontal` (CoordinateSystem) [property] L2376 - NEEDS_PARAMETERS -- ⚠️ `unit_horizontal_0` (CoordinateSystem) [property] L2328 +- ⚠️ `unit_horizontal_0` (CoordinateSystem) [property] L2392 - NEEDS_PARAMETERS -- ⚠️ `unit_horizontal_1` (CoordinateSystem) [property] L2333 +- ⚠️ `unit_horizontal_1` (CoordinateSystem) [property] L2397 - NEEDS_PARAMETERS -- ⚠️ `unit_radial` (CoordinateSystem) [property] L2350 +- ⚠️ `unit_radial` (CoordinateSystem) [property] L2414 - NEEDS_PARAMETERS -- ⚠️ `unit_tangential` (CoordinateSystem) [property] L2365 +- ⚠️ `unit_tangential` (CoordinateSystem) [property] L2429 - NEEDS_PARAMETERS -- ⚠️ `unit_meridional` (CoordinateSystem) [property] L2378 +- ⚠️ `unit_meridional` (CoordinateSystem) [property] L2442 - NEEDS_PARAMETERS -- ⚠️ `unit_azimuthal` (CoordinateSystem) [property] L2388 +- ⚠️ `unit_azimuthal` (CoordinateSystem) [property] L2452 - NEEDS_PARAMETERS -- ⚠️ `geometric_dimension_names` (CoordinateSystem) [property] L2398 +- ⚠️ `geometric_dimension_names` (CoordinateSystem) [property] L2462 - NEEDS_PARAMETERS -- ⚠️ `primary_directions` (CoordinateSystem) [property] L2415 +- ⚠️ `primary_directions` (CoordinateSystem) [property] L2479 - NEEDS_PARAMETERS -- ✅ `create_line_sample` (CoordinateSystem) [method] L2463 -- ✅ `create_profile_sample` (CoordinateSystem) [method] L2588 -- 📝 `zero_matrix` (CoordinateSystem) [method] L2847 +- ✅ `create_line_sample` (CoordinateSystem) [method] L2527 +- ✅ `create_profile_sample` (CoordinateSystem) [method] L2652 +- 📝 `zero_matrix` (CoordinateSystem) [method] L2911 - NEEDS_PARAMETERS, NEEDS_RETURNS
Private/Internal (45 items) - `__init__` [method] L93 - none -- `__init__` [method] L1379 - none -- `__init__` [method] L529 - partial -- `__init__` [method] L989 - partial +- `__init__` [method] L1351 - none +- `__init__` [method] L502 - partial +- `__init__` [method] L956 - partial - `__new__` [method] L88 - none - `__repr__` [method] L248 - none - `__eq__` [method] L107 - partial @@ -1194,165 +1564,261 @@ Generated by `docstring_sweep.py` - `_ccodestr` [method] L235 - minimal - `_ccode` [method] L239 - partial - `_latex` [method] L251 - minimal -- `_invalidate_cache` [method] L548 - minimal -- `_compute_coordinates` [method] L552 - minimal -- `__getitem__` [method] L653 - partial -- `__repr__` [method] L866 - minimal -- `_invalidate_cache` [method] L1008 - minimal -- `_compute_coordinates` [method] L1012 - minimal -- `__getitem__` [method] L1100 - partial -- `__repr__` [method] L1281 - minimal -- `_apply_units_scaling` [method] L1805 - minimal -- `__getitem__` [method] L1861 - minimal -- `__iter__` [method] L1865 - minimal -- `__len__` [method] L1869 - minimal -- `_sympy_` [method] L1947 - partial -- `__sympy__` [method] L1959 - minimal -- `__getattr__` [method] L1991 - partial -- `__add__` [method] L2024 - minimal -- `__radd__` [method] L2028 - minimal -- `__sub__` [method] L2032 - minimal -- `__rsub__` [method] L2036 - minimal -- `__mul__` [method] L2040 - minimal -- `__rmul__` [method] L2044 - minimal -- `__truediv__` [method] L2048 - minimal -- `__rtruediv__` [method] L2052 - minimal -- `__pow__` [method] L2056 - minimal -- `__neg__` [method] L2060 - minimal -- `_cartesian_to_natural_coords` [method] L2528 - complete -- `_create_cartesian_profile` [method] L2622 - minimal -- `_create_cylindrical_profile` [method] L2695 - minimal -- `_create_spherical_profile` [method] L2763 - minimal +- `_invalidate_cache` [method] L521 - minimal +- `_compute_coordinates` [method] L525 - minimal +- `__getitem__` [method] L629 - partial +- `__repr__` [method] L846 - minimal +- `_invalidate_cache` [method] L980 - minimal +- `_compute_coordinates` [method] L984 - minimal +- `__getitem__` [method] L1072 - partial +- `__repr__` [method] L1253 - minimal +- `_apply_units_scaling` [method] L1792 - minimal +- `__getitem__` [method] L1848 - minimal +- `__iter__` [method] L1852 - minimal +- `__len__` [method] L1856 - minimal +- `_sympy_` [method] L1976 - partial +- `__sympy__` [method] L1988 - minimal +- `__getattr__` [method] L2020 - partial +- `__add__` [method] L2053 - minimal +- `__radd__` [method] L2057 - minimal +- `__sub__` [method] L2061 - minimal +- `__rsub__` [method] L2065 - minimal +- `__mul__` [method] L2069 - minimal +- `__rmul__` [method] L2073 - minimal +- `__truediv__` [method] L2077 - minimal +- `__rtruediv__` [method] L2081 - minimal +- `__pow__` [method] L2085 - minimal +- `__neg__` [method] L2089 - minimal +- `_cartesian_to_natural_coords` [method] L2592 - complete +- `_create_cartesian_profile` [method] L2686 - minimal +- `_create_cylindrical_profile` [method] L2759 - minimal +- `_create_spherical_profile` [method] L2827 - minimal
-### `src/underworld3/discretisation/discretisation_mesh.py` +### `src/underworld3/cython/petsc_maths.pyx` **Public API:** -- 📝 `Mesh` [class] L149 +- 📝 `Integral` [class] L34 + - NEEDS_RETURNS +- ✅ `CellWiseIntegral` [class] L227 +- 📝 `BdIntegral` [class] L340 - NEEDS_RETURNS -- ❌ `extend_enum` [function] L34 +- ❌ `has_meaningful_units` [method] L148 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `nuke_coords_and_rebuild` (Mesh) [method] L1087 +- ❌ `has_meaningful_units` [method] L482 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- 📝 `dm_force_coordinate_field` [function] L18 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `evaluate` [method] L71 + - NEEDS_PARAMETERS +- 📝 `evaluate` [method] L262 + - NEEDS_PARAMETERS +- 📝 `evaluate` [method] L399 + - NEEDS_PARAMETERS + +
+Private/Internal (3 items) + +- `__init__` [method] L62 - none +- `__init__` [method] L253 - none +- `__init__` [method] L379 - none + +
+ +### `src/underworld3/discretisation/discretisation_mesh.py` + +**Public API:** + +- 📝 `Mesh` [class] L207 + - NEEDS_RETURNS +- ❌ `extend_enum` [function] L82 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `checkpoint_xdmf` [function] L3124 +- ❌ `checkpoint_xdmf` [function] L6232 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- 📝 `dim` (Mesh) [property] L735 +- 📝 `dim` (Mesh) [property] L1092 + - NEEDS_PARAMETERS +- 📝 `cdim` (Mesh) [property] L1103 - NEEDS_PARAMETERS -- 📝 `cdim` (Mesh) [property] L746 +- 📝 `element` (Mesh) [property] L1117 - NEEDS_PARAMETERS -- 📝 `element` (Mesh) [property] L760 +- 📝 `length_scale` (Mesh) [property] L1137 - NEEDS_PARAMETERS -- 📝 `length_scale` (Mesh) [property] L780 +- 📝 `length_units` (Mesh) [property] L1168 - NEEDS_PARAMETERS -- 📝 `length_units` (Mesh) [property] L811 +- ✅ `quality` (Mesh) [method] L1184 +- 📝 `view` (Mesh) [method] L1337 + - NEEDS_RETURNS +- ⚠️ `view_parallel` (Mesh) [method] L1556 + - NEEDS_PARAMETERS +- ⚠️ `clone_dm_hierarchy` (Mesh) [method] L1607 - NEEDS_PARAMETERS -- 📝 `view` (Mesh) [method] L827 +- ✅ `extract_region` (Mesh) [method] L1623 +- ✅ `extract_surface` (Mesh) [method] L1733 +- 📝 `sync_coordinates_from_parent` (Mesh) [method] L1949 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `restrict` (Mesh) [method] L2155 + - NEEDS_RETURNS +- 📝 `prolongate` (Mesh) [method] L2195 - NEEDS_RETURNS -- ⚠️ `view_parallel` (Mesh) [method] L1020 +- 📝 `nuke_coords_and_rebuild` (Mesh) [method] L2235 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `boundary_normal` (Mesh) [method] L2504 +- 📝 `cell_size` (Mesh) [method] L2612 - NEEDS_PARAMETERS -- ⚠️ `clone_dm_hierarchy` (Mesh) [method] L1071 +- 📝 `Gamma_P1` (Mesh) [property] L2698 - NEEDS_PARAMETERS -- ⚠️ `update_lvec` (Mesh) [method] L1189 +- 📝 `bounding_surfaces` (Mesh) [property] L2718 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `register_tangent_slip_provider` (Mesh) [method] L2732 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `restore_to_surface` (Mesh) [method] L2772 - NEEDS_PARAMETERS -- ⚠️ `lvec` (Mesh) [property] L1228 +- ⚠️ `tangent_project` (Mesh) [method] L2778 - NEEDS_PARAMETERS -- ✅ `access` (Mesh) [method] L1371 -- 📝 `N` (Mesh) [property] L1421 +- ✅ `boundary_slip` (Mesh) [method] L2785 +- 📝 `project_to_slip_surface` (Mesh) [method] L2926 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `update_lvec` (Mesh) [method] L2938 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `lvec` (Mesh) [property] L3004 - NEEDS_PARAMETERS -- 📝 `Gamma_N` (Mesh) [property] L1441 +- 📝 `register_remesh_hook` (Mesh) [method] L3017 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `unregister_remesh_hook` (Mesh) [method] L3049 - NEEDS_PARAMETERS -- 📝 `Gamma` (Mesh) [property] L1452 +- 📝 `ephemeral_coords` (Mesh) [method] L3114 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `deform` (Mesh) [method] L3133 +- ✅ `access` (Mesh) [method] L3386 +- 📝 `N` (Mesh) [property] L3436 - NEEDS_PARAMETERS -- 📝 `X` (Mesh) [property] L1463 +- 📝 `Gamma_N` (Mesh) [property] L3456 - NEEDS_PARAMETERS -- ⚠️ `CoordinateSystem` (Mesh) [property] L1491 +- 📝 `Gamma` (Mesh) [property] L3472 - NEEDS_PARAMETERS -- 📝 `r` (Mesh) [property] L1496 +- 📝 `X` (Mesh) [property] L3486 - NEEDS_PARAMETERS -- 📝 `rvec` (Mesh) [property] L1511 +- ⚠️ `CoordinateSystem` (Mesh) [property] L3514 - NEEDS_PARAMETERS -- 📝 `data` (Mesh) [property] L1531 +- 📝 `t` (Mesh) [property] L3519 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `points` (Mesh) [property] L1550 +- 📝 `nullspace_rotations` (Mesh) [property] L3545 - NEEDS_PARAMETERS -- 📝 `points` (Mesh) [method] L1592 - - NEEDS_RETURNS -- 📝 `physical_coordinates` (Mesh) [property] L1661 +- 📝 `r` (Mesh) [property] L3570 - NEEDS_PARAMETERS -- 📝 `physical_bounds` (Mesh) [property] L1686 +- 📝 `rvec` (Mesh) [property] L3585 - NEEDS_PARAMETERS -- 📝 `physical_extent` (Mesh) [property] L1717 +- 📝 `data` (Mesh) [property] L3605 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `points` (Mesh) [property] L3624 - NEEDS_PARAMETERS -- 📝 `write_timestep` (Mesh) [method] L1745 +- 📝 `points` (Mesh) [method] L3666 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `petsc_save_checkpoint` (Mesh) [method] L1816 +- 📝 `physical_coordinates` (Mesh) [property] L3685 + - NEEDS_PARAMETERS +- 📝 `physical_bounds` (Mesh) [property] L3710 + - NEEDS_PARAMETERS +- 📝 `physical_extent` (Mesh) [property] L3741 + - NEEDS_PARAMETERS +- 📝 `write_timestep` (Mesh) [method] L3769 - NEEDS_RETURNS -- 📝 `write_checkpoint` (Mesh) [method] L1862 +- 📝 `petsc_save_checkpoint` (Mesh) [method] L3903 + - NEEDS_RETURNS +- 📝 `write_checkpoint` (Mesh) [method] L4004 + - NEEDS_RETURNS +- 📝 `snapshot_payload` (Mesh) [method] L4144 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `write` (Mesh) [method] L1924 +- 📝 `apply_snapshot_payload` (Mesh) [method] L4177 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `write` (Mesh) [method] L4241 - NEEDS_RETURNS -- ⚠️ `vtk` (Mesh) [method] L1989 +- ⚠️ `vtk` (Mesh) [method] L4371 - NEEDS_PARAMETERS -- 📝 `generate_xdmf` (Mesh) [method] L1998 +- 📝 `generate_xdmf` (Mesh) [method] L4380 - NEEDS_RETURNS -- ⚠️ `vars` (Mesh) [property] L2020 +- ⚠️ `vars` (Mesh) [property] L4402 - NEEDS_PARAMETERS -- ⚠️ `block_vars` (Mesh) [property] L2029 +- ⚠️ `block_vars` (Mesh) [property] L4411 - NEEDS_PARAMETERS -- 📝 `points_in_domain` (Mesh) [method] L2475 +- 📝 `points_in_domain` (Mesh) [method] L5078 - NEEDS_RETURNS -- ✅ `get_closest_cells` (Mesh) [method] L2529 -- ✅ `test_if_points_in_cells` (Mesh) [method] L2646 -- ✅ `get_closest_local_cells` (Mesh) [method] L2681 -- 📝 `get_min_radius_old` (Mesh) [method] L2779 +- ✅ `get_closest_cells` (Mesh) [method] L5165 +- ✅ `test_if_points_in_cells` (Mesh) [method] L5326 +- ✅ `get_closest_local_cells` (Mesh) [method] L5381 +- 📝 `get_min_radius_old` (Mesh) [method] L5575 - NEEDS_PARAMETERS -- 📝 `get_min_radius` (Mesh) [method] L2796 +- 📝 `get_min_radius` (Mesh) [method] L5593 - NEEDS_PARAMETERS -- ⚠️ `get_max_radius` (Mesh) [method] L2812 +- ⚠️ `get_max_radius` (Mesh) [method] L5610 - NEEDS_PARAMETERS -- 📝 `stats` (Mesh) [method] L2827 +- 📝 `get_mean_radius` (Mesh) [method] L5625 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `stats` (Mesh) [method] L5654 - NEEDS_PARAMETERS -- ⚠️ `meshVariable_mask_from_label` (Mesh) [method] L2863 +- ⚠️ `meshVariable_mask_from_label` (Mesh) [method] L5690 - NEEDS_PARAMETERS -- ⚠️ `register_swarm` (Mesh) [method] L2889 +- ⚠️ `register_swarm` (Mesh) [method] L5716 - NEEDS_PARAMETERS -- ⚠️ `unregister_swarm` (Mesh) [method] L2893 +- ⚠️ `unregister_swarm` (Mesh) [method] L5720 - NEEDS_PARAMETERS -- ⚠️ `register_surface` (Mesh) [method] L2898 +- ⚠️ `register_surface` (Mesh) [method] L5725 - NEEDS_PARAMETERS -- ⚠️ `unregister_surface` (Mesh) [method] L2902 +- ⚠️ `unregister_surface` (Mesh) [method] L5729 - NEEDS_PARAMETERS -- 📝 `adapt` (Mesh) [method] L2917 +- ✅ `OT_adapt` (Mesh) [method] L5744 +- 📝 `OT_adapt_reset_reference` (Mesh) [method] L5883 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `adapt` (Mesh) [method] L5899 - NEEDS_RETURNS -- 📝 `meshVariable_lookup_by_symbol` [function] L3342 +- 📝 `meshVariable_lookup_by_symbol` [function] L6517 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `petsc_dm_find_labeled_points_local` [function] L3359 +- 📝 `petsc_dm_find_labeled_points_local` [function] L6534 - NEEDS_PARAMETERS, NEEDS_RETURNS
-Private/Internal (19 items) - -- `__init__` [method] L208 - none -- `__del__` [method] L1237 - none -- `_build_kd_tree_index_DS` [method] L2095 - none -- `_build_kd_tree_index` [method] L2118 - none -- `_build_kd_tree_index_PIC` [method] L2200 - none -- `_get_domain_centroids` [method] L2771 - none -- `_from_gmsh` [function] L47 - partial -- `_from_plexh5` [function] L117 - partial -- `_deform_mesh` [method] L1241 - partial -- `_legacy_access` [method] L1258 - partial -- `_get_coords_for_var` [method] L2035 - partial -- `_get_coords_for_basis` [method] L2050 - partial -- `_mark_faces_inside_and_out` [method] L2256 - partial -- `_test_if_points_in_cells_internal` [method] L2351 - partial -- `_mark_local_boundary_faces_inside_and_out` [method] L2390 - partial -- `_get_closest_local_cells_internal` [method] L2573 - complete -- `_get_mesh_sizes` [method] L2718 - minimal -- `_get_mesh_centroids` [method] L2752 - partial -- `_increment_mesh_version` [method] L2906 - partial +Private/Internal (39 items) + +- `__init__` [method] L257 - none +- `_temporary_petsc_option` [function] L32 - none +- `__del__` [method] L3013 - none +- `_build_kd_tree_index_DS` [method] L4483 - none +- `_build_kd_tree_index` [method] L4506 - none +- `_build_kd_tree_index_PIC` [method] L4597 - none +- `_get_domain_centroids` [method] L5554 - none +- `_get_domain_kdtree` [method] L5563 - none +- `_clear_gmsh_import_options` [function] L65 - minimal +- `_from_gmsh` [function] L95 - partial +- `_from_plexh5` [function] L160 - partial +- `_hierarchy_sidecar_name` [function] L193 - partial +- `_build_vertex_map` [method] L1916 - partial +- `_re_extract_from_parent` [method] L1975 - partial +- `_build_dof_map` [method] L2122 - partial +- `_update_projected_normals` [method] L2467 - partial +- `_assemble_boundary_normal` [method] L2537 - minimal +- `_cell_size_var` [method] L2644 - partial +- `_assemble_cell_size` [method] L2674 - partial +- `_resolve_slip_spec` [method] L2747 - partial +- `_assert_coord_mutation_allowed` [method] L3063 - partial +- `_coord_mutation` [method] L3099 - partial +- `_deform_mesh` [method] L3189 - partial +- `_legacy_access` [method] L3273 - partial +- `_write_petsc_reload_variable` [method] L3961 - minimal +- `_write_petsc_reload_file` [method] L3980 - minimal +- `_get_coords_for_var` [method] L4417 - partial +- `_get_coords_for_basis` [method] L4432 - partial +- `_mark_faces_inside_and_out` [method] L4653 - partial +- `_get_owned_cells_mask` [method] L4771 - partial +- `_test_if_points_in_cells_internal` [method] L4813 - partial +- `_mark_local_boundary_faces_inside_and_out` [method] L4928 - partial +- `_get_closest_local_cells_internal` [method] L5209 - complete +- `_robust_owning_cells` [method] L5446 - partial +- `_eval_use_robust_location` [method] L5473 - partial +- `_get_mesh_sizes` [method] L5501 - minimal +- `_get_mesh_centroids` [method] L5535 - partial +- `_increment_mesh_version` [method] L5733 - partial +- `_write_compat_groups` [function] L6181 - partial
@@ -1362,6 +1828,8 @@ Generated by `docstring_sweep.py` - 📝 `EnhancedMeshVariable` [class] L37 - NEEDS_PARAMETERS, NEEDS_RETURNS +- ❌ `remesh_policy` (EnhancedMeshVariable) [method] L378 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS - ⚠️ `data` (EnhancedMeshVariable) [property] L244 - NEEDS_PARAMETERS - 📝 `array` (EnhancedMeshVariable) [property] L249 @@ -1398,244 +1866,343 @@ Generated by `docstring_sweep.py` - NEEDS_PARAMETERS - ⚠️ `continuous` (EnhancedMeshVariable) [property] L363 - NEEDS_PARAMETERS -- ⚠️ `symbol` (EnhancedMeshVariable) [property] L368 +- 📝 `remesh_policy` (EnhancedMeshVariable) [property] L368 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `symbol` (EnhancedMeshVariable) [property] L398 + - NEEDS_PARAMETERS +- ⚠️ `vec` (EnhancedMeshVariable) [property] L405 - NEEDS_PARAMETERS -- ⚠️ `vec` (EnhancedMeshVariable) [property] L375 +- ⚠️ `sym` (EnhancedMeshVariable) [property] L426 - NEEDS_PARAMETERS -- ⚠️ `sym` (EnhancedMeshVariable) [property] L392 +- ⚠️ `pack_uw_data_to_petsc` (EnhancedMeshVariable) [method] L438 - NEEDS_PARAMETERS -- ⚠️ `pack_uw_data_to_petsc` (EnhancedMeshVariable) [method] L404 +- ⚠️ `unpack_uw_data_from_petsc` (EnhancedMeshVariable) [method] L442 - NEEDS_PARAMETERS -- ⚠️ `unpack_uw_data_from_petsc` (EnhancedMeshVariable) [method] L408 +- ⚠️ `divergence` (EnhancedMeshVariable) [method] L448 - NEEDS_PARAMETERS -- ⚠️ `divergence` (EnhancedMeshVariable) [method] L414 +- ⚠️ `gradient` (EnhancedMeshVariable) [method] L452 - NEEDS_PARAMETERS -- ⚠️ `gradient` (EnhancedMeshVariable) [method] L418 +- ⚠️ `curl` (EnhancedMeshVariable) [method] L456 - NEEDS_PARAMETERS -- ⚠️ `curl` (EnhancedMeshVariable) [method] L422 +- ⚠️ `jacobian` (EnhancedMeshVariable) [method] L460 - NEEDS_PARAMETERS -- ⚠️ `jacobian` (EnhancedMeshVariable) [method] L426 +- ⚠️ `clone` (EnhancedMeshVariable) [method] L466 - NEEDS_PARAMETERS -- ⚠️ `clone` (EnhancedMeshVariable) [method] L432 +- ⚠️ `max` (EnhancedMeshVariable) [method] L470 - NEEDS_PARAMETERS -- ⚠️ `max` (EnhancedMeshVariable) [method] L436 +- ⚠️ `mean` (EnhancedMeshVariable) [method] L474 - NEEDS_PARAMETERS -- ⚠️ `mean` (EnhancedMeshVariable) [method] L440 +- ⚠️ `min` (EnhancedMeshVariable) [method] L478 - NEEDS_PARAMETERS -- ⚠️ `min` (EnhancedMeshVariable) [method] L444 +- ⚠️ `norm` (EnhancedMeshVariable) [method] L482 - NEEDS_PARAMETERS -- ⚠️ `norm` (EnhancedMeshVariable) [method] L448 +- ⚠️ `load_from_h5_plex_vector` (EnhancedMeshVariable) [method] L491 - NEEDS_PARAMETERS -- ⚠️ `load_from_h5_plex_vector` (EnhancedMeshVariable) [method] L457 +- ⚠️ `read_checkpoint` (EnhancedMeshVariable) [method] L495 - NEEDS_PARAMETERS -- ⚠️ `write` (EnhancedMeshVariable) [method] L461 +- ⚠️ `write` (EnhancedMeshVariable) [method] L499 - NEEDS_PARAMETERS -- ⚠️ `sym_1d` (EnhancedMeshVariable) [property] L466 +- ⚠️ `sym_1d` (EnhancedMeshVariable) [property] L504 - NEEDS_PARAMETERS -- ⚠️ `save` (EnhancedMeshVariable) [method] L470 +- ⚠️ `save` (EnhancedMeshVariable) [method] L508 - NEEDS_PARAMETERS -- ⚠️ `read_timestep` (EnhancedMeshVariable) [method] L474 +- ⚠️ `read_timestep` (EnhancedMeshVariable) [method] L512 - NEEDS_PARAMETERS -- ⚠️ `stats` (EnhancedMeshVariable) [method] L478 +- 📝 `copy_into` (EnhancedMeshVariable) [method] L516 + - NEEDS_RETURNS +- 📝 `add_into` (EnhancedMeshVariable) [method] L550 + - NEEDS_RETURNS +- ⚠️ `stats` (EnhancedMeshVariable) [method] L579 - NEEDS_PARAMETERS -- ⚠️ `sum` (EnhancedMeshVariable) [method] L482 +- ⚠️ `sum` (EnhancedMeshVariable) [method] L583 - NEEDS_PARAMETERS -- ⚠️ `rbf_interpolate` (EnhancedMeshVariable) [method] L486 +- ⚠️ `rbf_interpolate` (EnhancedMeshVariable) [method] L587 - NEEDS_PARAMETERS -- ⚠️ `pack_raw_data_to_petsc` (EnhancedMeshVariable) [method] L490 +- ⚠️ `pack_raw_data_to_petsc` (EnhancedMeshVariable) [method] L595 - NEEDS_PARAMETERS -- ⚠️ `unpack_raw_data_from_petsc` (EnhancedMeshVariable) [method] L494 +- ⚠️ `unpack_raw_data_from_petsc` (EnhancedMeshVariable) [method] L599 - NEEDS_PARAMETERS -- ⚠️ `field_id` (EnhancedMeshVariable) [property] L501 +- ⚠️ `field_id` (EnhancedMeshVariable) [property] L606 - NEEDS_PARAMETERS -- ⚠️ `fn` (EnhancedMeshVariable) [property] L506 +- ⚠️ `fn` (EnhancedMeshVariable) [property] L611 - NEEDS_PARAMETERS -- ⚠️ `ijk` (EnhancedMeshVariable) [property] L511 +- ⚠️ `ijk` (EnhancedMeshVariable) [property] L616 - NEEDS_PARAMETERS -- ⚠️ `instance_number` (EnhancedMeshVariable) [property] L516 +- ⚠️ `instance_number` (EnhancedMeshVariable) [property] L621 - NEEDS_PARAMETERS -- ⚠️ `old_data` (EnhancedMeshVariable) [property] L521 +- ⚠️ `old_data` (EnhancedMeshVariable) [property] L626 - NEEDS_PARAMETERS -- ⚠️ `uw_object_counter` (EnhancedMeshVariable) [property] L526 +- ⚠️ `uw_object_counter` (EnhancedMeshVariable) [property] L631 - NEEDS_PARAMETERS -- ✅ `transfer_data_from` (EnhancedMeshVariable) [method] L532 -- 📝 `qualified_name` (EnhancedMeshVariable) [property] L563 +- ✅ `transfer_data_from` (EnhancedMeshVariable) [method] L637 +- 📝 `qualified_name` (EnhancedMeshVariable) [property] L668 - NEEDS_PARAMETERS -- ⚠️ `is_persistent` (EnhancedMeshVariable) [property] L573 +- ⚠️ `is_persistent` (EnhancedMeshVariable) [property] L678 - NEEDS_PARAMETERS -- ⚠️ `view` (EnhancedMeshVariable) [method] L597 +- ⚠️ `view` (EnhancedMeshVariable) [method] L702 - NEEDS_PARAMETERS -- ✅ `create_enhanced_mesh_variable` [function] L638 -- 📝 `demonstrate_enhanced_variables` [function] L702 +- ✅ `create_enhanced_mesh_variable` [function] L743 +- 📝 `demonstrate_enhanced_variables` [function] L807 - NEEDS_PARAMETERS, NEEDS_RETURNS
-Private/Internal (10 items) +Private/Internal (14 items) - `__init__` [method] L90 - partial +- `_remesh_managed_by` [method] L394 - none - `__new__` [method] L66 - minimal - `_setup_registration` [method] L197 - minimal - `_auto_derive_scaling_coefficient` [method] L213 - minimal - `_setup_persistence_features` [method] L229 - minimal -- `_lvec` [property] L380 - minimal -- `_gvec` [property] L385 - minimal -- `_set_vec` [method] L400 - minimal -- `__repr__` [method] L579 - minimal -- `__str__` [method] L593 - minimal +- `_remesh_managed_by` [property] L382 - partial +- `_lvec` [property] L410 - minimal +- `_gvec` [property] L415 - minimal +- `_sync_lvec_to_gvec` [method] L419 - minimal +- `_set_vec` [method] L434 - minimal +- `_get_kdtree` [method] L591 - minimal +- `__repr__` [method] L684 - minimal +- `__str__` [method] L698 - minimal
-### `src/underworld3/function/dminterpolation_cache.py` +### `src/underworld3/discretisation/remesh.py` **Public API:** -- 📝 `DMInterpolationCache` [class] L24 +- 📝 `RemeshPolicy` [class] L56 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ✅ `get_structure` (DMInterpolationCache) [method] L53 -- 📝 `store_structure` (DMInterpolationCache) [method] L88 - - NEEDS_RETURNS -- 📝 `invalidate_all` (DMInterpolationCache) [method] L120 +- 📝 `RemeshContext` [class] L97 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `invalidate_coords` (DMInterpolationCache) [method] L133 - - NEEDS_PARAMETERS -- 📝 `get_stats` (DMInterpolationCache) [method] L144 +- ⚠️ `total_disp` (RemeshContext) [property] L127 - NEEDS_PARAMETERS -- ⚠️ `print_stats` (DMInterpolationCache) [method] L169 - - NEEDS_PARAMETERS -- ⚠️ `reset_stats` (DMInterpolationCache) [method] L183 +- 📝 `remesh_with_field_transfer` [function] L218 - NEEDS_PARAMETERS
-Private/Internal (3 items) +Private/Internal (8 items) -- `__init__` [method] L34 - none -- `_is_enabled` [method] L48 - minimal -- `_hash_coords` [method] L109 - minimal +- `_gather_transfer_vars` [function] L132 - partial +- `_snapshot_remap_data` [function] L173 - minimal +- `_remap_one_var` [function] L190 - partial +- `_new_coord_cache` [function] L200 - partial +- `_remesh_with_field_transfer_impl` [function] L290 - minimal +- `_iter_active_hooks` [function] L358 - partial +- `_remap_var_set` [function] L385 - partial +- `_mark_reinit_stale` [function] L478 - partial
-### `src/underworld3/function/expressions.py` +### `src/underworld3/function/_dminterp_wrapper.pyx` **Public API:** -- ✅ `UWexpression` [class] L533 -- 📝 `UWDerivativeExpression` [class] L1657 +- 📝 `CachedDMInterpolationInfo` [class] L40 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `create_structure` [method] L79 - NEEDS_RETURNS -- ❌ `description` (UWexpression) [property] L907 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `description` (UWexpression) [method] L911 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ✅ `simplify_units` [function] L31 -- ✅ `unwrap_expression` [function] L176 -- ✅ `is_constant_expr` [function] L227 -- ✅ `extract_expressions` [function] L247 -- ✅ `extract_expressions_and_functions` [function] L282 -- ✅ `expand` [function] L358 -- ✅ `unwrap` [function] L382 -- 📝 `unwrap_for_evaluate` [function] L403 - - NEEDS_PARAMETERS -- ⚠️ `substitute_expr` [function] L518 - - NEEDS_PARAMETERS -- ✅ `rename` (UWexpression) [method] L664 -- ⚠️ `sym` (UWexpression) [property] L802 - - NEEDS_PARAMETERS -- ⚠️ `sym` (UWexpression) [method] L807 +- ✅ `evaluate` [method] L175 + +
+Private/Internal (3 items) + +- `__repr__` [method] L234 - none +- `__cinit__` [method] L72 - minimal +- `__dealloc__` [method] L222 - partial + +
+ +### `src/underworld3/function/_function.pyx` + +**Public API:** + +- 📝 `UnderworldAppliedFunction` [class] L51 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `UnderworldAppliedFunctionDeriv` [class] L109 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `UnderworldFunction` [class] L134 +- 📝 `fdiff` [method] L77 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `fdiff` [method] L130 - NEEDS_PARAMETERS -- 📝 `copy` (UWexpression) [method] L819 +- ✅ `global_evaluate_nd` [function] L350 +- 📝 `evaluate_nd` [function] L901 - NEEDS_RETURNS -- ⚠️ `value` (UWexpression) [property] L855 - - NEEDS_PARAMETERS -- ⚠️ `data` (UWexpression) [property] L863 - - NEEDS_PARAMETERS -- ⚠️ `units` (UWexpression) [property] L881 - - NEEDS_PARAMETERS -- ⚠️ `has_units` (UWexpression) [property] L889 - - NEEDS_PARAMETERS -- ⚠️ `dimensionality` (UWexpression) [property] L894 - - NEEDS_PARAMETERS -- ⚠️ `expression` (UWexpression) [property] L902 - - NEEDS_PARAMETERS -- ⚠️ `expression_number` (UWexpression) [property] L915 - - NEEDS_PARAMETERS -- 📝 `quantity` (UWexpression) [property] L924 - - NEEDS_PARAMETERS -- ✅ `to` (UWexpression) [method] L941 -- 📝 `to_base_units` (UWexpression) [method] L966 +- 📝 `petsc_interpolate` [function] L1091 + - NEEDS_RETURNS +- ⚠️ `interpolate_vars_on_mesh` [method] L1222 - NEEDS_PARAMETERS -- 📝 `to_reduced_units` (UWexpression) [method] L986 +- 📝 `rbf_evaluate` [function] L1381 + - NEEDS_RETURNS +- ✅ `dm_swarm_get_migrate_type` [function] L1503 +- 📝 `dm_swarm_set_migrate_type` [function] L1528 + - NEEDS_RETURNS + +
+Private/Internal (8 items) + +- `_latex` [method] L91 - none +- `__new__` [method] L170 - none +- `_dmswarm_get_migrate_type` [function] L1550 - none +- `_dmswarm_set_migrate_type` [function] L1560 - none +- `_collect_mesh_varfns` [function] L222 - complete +- `_lambdify_and_evaluate` [function] L244 - complete +- `_project_to_work_variable` [function] L648 - complete +- `_clement_to_work_variable` [function] L772 - complete + +
+ +### `src/underworld3/function/dminterpolation_cache.py` + +**Public API:** + +- 📝 `DMInterpolationCache` [class] L24 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `get_structure` (DMInterpolationCache) [method] L55 +- 📝 `store_structure` (DMInterpolationCache) [method] L91 + - NEEDS_RETURNS +- 📝 `invalidate_all` (DMInterpolationCache) [method] L130 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `invalidate_coords` (DMInterpolationCache) [method] L143 + - NEEDS_PARAMETERS +- 📝 `get_stats` (DMInterpolationCache) [method] L154 + - NEEDS_PARAMETERS +- ⚠️ `print_stats` (DMInterpolationCache) [method] L179 + - NEEDS_PARAMETERS +- ⚠️ `reset_stats` (DMInterpolationCache) [method] L193 + - NEEDS_PARAMETERS + +
+Private/Internal (3 items) + +- `__init__` [method] L34 - none +- `_is_enabled` [method] L50 - minimal +- `_hash_coords` [method] L119 - minimal + +
+ +### `src/underworld3/function/expressions.py` + +**Public API:** + +- ✅ `UWexpression` [class] L605 +- 📝 `UWDerivativeExpression` [class] L1746 + - NEEDS_RETURNS +- ❌ `description` (UWexpression) [property] L996 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `description` (UWexpression) [method] L1000 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ✅ `simplify_units` [function] L31 +- ✅ `unwrap_expression` [function] L201 +- ✅ `is_constant_expr` [function] L255 +- ✅ `extract_expressions` [function] L275 +- ✅ `extract_meshes` [function] L310 +- ✅ `extract_expressions_and_functions` [function] L354 +- ✅ `expand` [function] L430 +- ✅ `unwrap` [function] L454 +- 📝 `unwrap_for_evaluate` [function] L475 + - NEEDS_PARAMETERS +- ⚠️ `substitute_expr` [function] L590 + - NEEDS_PARAMETERS +- ✅ `rename` (UWexpression) [method] L753 +- ⚠️ `sym` (UWexpression) [property] L891 + - NEEDS_PARAMETERS +- ⚠️ `sym` (UWexpression) [method] L896 + - NEEDS_PARAMETERS +- 📝 `copy` (UWexpression) [method] L908 + - NEEDS_RETURNS +- ⚠️ `value` (UWexpression) [property] L944 + - NEEDS_PARAMETERS +- ⚠️ `data` (UWexpression) [property] L952 + - NEEDS_PARAMETERS +- ⚠️ `units` (UWexpression) [property] L970 + - NEEDS_PARAMETERS +- ⚠️ `has_units` (UWexpression) [property] L978 + - NEEDS_PARAMETERS +- ⚠️ `dimensionality` (UWexpression) [property] L983 + - NEEDS_PARAMETERS +- ⚠️ `expression` (UWexpression) [property] L991 + - NEEDS_PARAMETERS +- ⚠️ `expression_number` (UWexpression) [property] L1004 - NEEDS_PARAMETERS -- 📝 `to_compact` (UWexpression) [method] L1000 +- 📝 `quantity` (UWexpression) [property] L1013 - NEEDS_PARAMETERS -- ⚠️ `atoms` (UWexpression) [method] L1035 +- ✅ `to` (UWexpression) [method] L1030 +- 📝 `to_base_units` (UWexpression) [method] L1055 - NEEDS_PARAMETERS -- ⚠️ `is_number` (UWexpression) [property] L1056 +- 📝 `to_reduced_units` (UWexpression) [method] L1075 - NEEDS_PARAMETERS -- ⚠️ `is_comparable` (UWexpression) [property] L1061 +- 📝 `to_compact` (UWexpression) [method] L1089 - NEEDS_PARAMETERS -- ⚠️ `is_extended_real` (UWexpression) [property] L1068 +- ⚠️ `atoms` (UWexpression) [method] L1124 - NEEDS_PARAMETERS -- ⚠️ `is_positive` (UWexpression) [property] L1075 +- ⚠️ `is_number` (UWexpression) [property] L1145 - NEEDS_PARAMETERS -- ⚠️ `is_negative` (UWexpression) [property] L1082 +- ⚠️ `is_comparable` (UWexpression) [property] L1150 - NEEDS_PARAMETERS -- ⚠️ `is_zero` (UWexpression) [property] L1089 +- ⚠️ `is_extended_real` (UWexpression) [property] L1157 - NEEDS_PARAMETERS -- ⚠️ `is_finite` (UWexpression) [property] L1096 +- ⚠️ `is_positive` (UWexpression) [property] L1164 - NEEDS_PARAMETERS -- ⚠️ `is_constant` (UWexpression) [method] L1102 +- ⚠️ `is_negative` (UWexpression) [property] L1171 - NEEDS_PARAMETERS -- ⚠️ `is_uw_constant` (UWexpression) [method] L1106 +- ⚠️ `is_zero` (UWexpression) [property] L1178 - NEEDS_PARAMETERS -- ⚠️ `constant` (UWexpression) [method] L1110 +- ⚠️ `is_finite` (UWexpression) [property] L1185 - NEEDS_PARAMETERS -- ⚠️ `diff` (UWexpression) [method] L1114 +- ⚠️ `is_constant` (UWexpression) [method] L1191 - NEEDS_PARAMETERS -- 📝 `doit` (UWDerivativeExpression) [method] L1684 +- ⚠️ `is_uw_constant` (UWexpression) [method] L1195 - NEEDS_PARAMETERS -- 📝 `mesh_vars_in_expression` [function] L1702 +- ⚠️ `constant` (UWexpression) [method] L1199 + - NEEDS_PARAMETERS +- ⚠️ `diff` (UWexpression) [method] L1203 + - NEEDS_PARAMETERS +- 📝 `doit` (UWDerivativeExpression) [method] L1773 + - NEEDS_PARAMETERS +- 📝 `mesh_vars_in_expression` [function] L1791 - NEEDS_PARAMETERS
Private/Internal (38 items) -- `__init__` [method] L741 - none -- `__init__` [method] L1679 - none -- `__new__` [method] L583 - none +- `__init__` [method] L830 - none +- `__init__` [method] L1768 - none +- `__new__` [method] L667 - none - `_unwrap_atom` [function] L76 - complete -- `_unwrap_expression_once` [function] L132 - complete -- `_unwrap_expressions` [function] L325 - partial -- `_unwrap_for_compilation` [function] L336 - minimal -- `_hashable_content` [method] L642 - partial -- `__getnewargs_ex__` [method] L655 - minimal -- `_latex` [method] L700 - complete -- `_sympystr` [method] L721 - complete -- `_compute_nondimensional_value` [method] L867 - minimal -- `_sympy_` [method] L1027 - minimal -- `_sympify_` [method] L1031 - minimal -- `__bool__` [method] L1039 - minimal -- `__hash__` [method] L1043 - minimal -- `__eq__` [method] L1047 - minimal -- `__ne__` [method] L1051 - minimal -- `__mul__` [method] L1122 - minimal -- `__rmul__` [method] L1164 - partial -- `__truediv__` [method] L1194 - minimal -- `__rtruediv__` [method] L1247 - minimal -- `__add__` [method] L1268 - minimal -- `__radd__` [method] L1344 - minimal -- `__sub__` [method] L1351 - partial -- `__rsub__` [method] L1391 - minimal -- `__pow__` [method] L1423 - minimal -- `__rpow__` [method] L1427 - minimal -- `__neg__` [method] L1431 - minimal -- `__repr__` [method] L1439 - partial -- `__str__` [method] L1479 - minimal -- `_repr_latex_` [method] L1488 - partial -- `_repr_html_` [method] L1533 - minimal -- `_repr_png_` [method] L1556 - partial -- `_repr_svg_` [method] L1566 - minimal -- `_repr_mimebundle_` [method] L1570 - partial -- `_ipython_display_` [method] L1596 - partial -- `_object_viewer` [method] L1618 - partial +- `_unwrap_expression_once` [function] L157 - complete +- `_unwrap_expressions` [function] L397 - partial +- `_unwrap_for_compilation` [function] L408 - minimal +- `_hashable_content` [method] L731 - partial +- `__getnewargs_ex__` [method] L744 - minimal +- `_latex` [method] L789 - complete +- `_sympystr` [method] L810 - complete +- `_compute_nondimensional_value` [method] L956 - minimal +- `_sympy_` [method] L1116 - minimal +- `_sympify_` [method] L1120 - minimal +- `__bool__` [method] L1128 - minimal +- `__hash__` [method] L1132 - minimal +- `__eq__` [method] L1136 - minimal +- `__ne__` [method] L1140 - minimal +- `__mul__` [method] L1211 - minimal +- `__rmul__` [method] L1253 - partial +- `__truediv__` [method] L1283 - minimal +- `__rtruediv__` [method] L1336 - minimal +- `__add__` [method] L1357 - minimal +- `__radd__` [method] L1433 - minimal +- `__sub__` [method] L1440 - partial +- `__rsub__` [method] L1480 - minimal +- `__pow__` [method] L1512 - minimal +- `__rpow__` [method] L1516 - minimal +- `__neg__` [method] L1520 - minimal +- `__repr__` [method] L1528 - partial +- `__str__` [method] L1568 - minimal +- `_repr_latex_` [method] L1577 - partial +- `_repr_html_` [method] L1622 - minimal +- `_repr_png_` [method] L1645 - partial +- `_repr_svg_` [method] L1655 - minimal +- `_repr_mimebundle_` [method] L1659 - partial +- `_ipython_display_` [method] L1685 - partial +- `_object_viewer` [method] L1707 - partial
@@ -1645,78 +2212,60 @@ Generated by `docstring_sweep.py` - 📝 `UWQuantity` [class] L22 - NEEDS_RETURNS -- 📝 `value` (UWQuantity) [property] L128 +- 📝 `value` (UWQuantity) [property] L171 - NEEDS_PARAMETERS -- 📝 `data` (UWQuantity) [property] L140 +- 📝 `data` (UWQuantity) [property] L183 - NEEDS_PARAMETERS -- ⚠️ `magnitude` (UWQuantity) [property] L193 +- ⚠️ `magnitude` (UWQuantity) [property] L236 - NEEDS_PARAMETERS -- 📝 `units` (UWQuantity) [property] L198 +- 📝 `units` (UWQuantity) [property] L241 - NEEDS_PARAMETERS -- ⚠️ `has_units` (UWQuantity) [property] L210 +- ⚠️ `has_units` (UWQuantity) [property] L253 - NEEDS_PARAMETERS -- 📝 `dimensionality` (UWQuantity) [property] L215 +- 📝 `dimensionality` (UWQuantity) [property] L258 - NEEDS_PARAMETERS -- ✅ `to` (UWQuantity) [method] L232 -- ⚠️ `to_base_units` (UWQuantity) [method] L252 +- ✅ `to` (UWQuantity) [method] L275 +- ⚠️ `to_base_units` (UWQuantity) [method] L301 - NEEDS_PARAMETERS -- ⚠️ `to_reduced_units` (UWQuantity) [method] L260 +- ⚠️ `to_reduced_units` (UWQuantity) [method] L309 - NEEDS_PARAMETERS -- ⚠️ `to_compact` (UWQuantity) [method] L268 +- ⚠️ `to_compact` (UWQuantity) [method] L317 - NEEDS_PARAMETERS -- ⚠️ `diff` (UWQuantity) [method] L751 +- ⚠️ `diff` (UWQuantity) [method] L800 - NEEDS_PARAMETERS -- ✅ `quantity` [function] L834 +- ✅ `quantity` [function] L883
-Private/Internal (27 items) +Private/Internal (28 items) - `__init__` [method] L53 - partial -- `__lt__` [method] L681 - none -- `__le__` [method] L688 - none -- `__gt__` [method] L695 - none -- `__ge__` [method] L702 - none -- `__eq__` [method] L709 - none -- `__ne__` [method] L716 - none -- `_from_pint` [method] L100 - complete -- `_compute_nd_value` [method] L161 - minimal -- `__add__` [method] L280 - minimal -- `__radd__` [method] L324 - minimal -- `__sub__` [method] L328 - minimal -- `__rsub__` [method] L389 - minimal -- `__mul__` [method] L421 - minimal -- `__rmul__` [method] L544 - minimal -- `__truediv__` [method] L548 - minimal -- `__rtruediv__` [method] L624 - partial -- `__pow__` [method] L661 - minimal -- `__neg__` [method] L669 - minimal -- `_sympy_` [method] L723 - partial -- `__float__` [method] L747 - minimal -- `__str__` [method] L759 - minimal -- `__repr__` [method] L765 - minimal -- `__format__` [method] L771 - minimal -- `_repr_latex_` [method] L786 - minimal -- `_repr_mimebundle_` [method] L807 - minimal -- `_ipython_display_` [method] L818 - minimal - -
- -### `src/underworld3/kdtree.py` - -**Public API:** - -- 📝 `KDTree` [class] L18 - - NEEDS_PARAMETERS -- ❌ `rbf_interpolator_local` (KDTree) [method] L128 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ✅ `query` (KDTree) [method] L149 -- ✅ `rbf_interpolator_local_from_kdtree` (KDTree) [method] L203 - -
-Private/Internal (2 items) - -- `__init__` [method] L38 - partial -- `_convert_coords_to_tree_units` [method] L68 - complete +- `__lt__` [method] L730 - none +- `__le__` [method] L737 - none +- `__gt__` [method] L744 - none +- `__ge__` [method] L751 - none +- `__eq__` [method] L758 - none +- `__ne__` [method] L765 - none +- `_is_offset_temperature` [method] L123 - partial +- `_from_pint` [method] L143 - complete +- `_compute_nd_value` [method] L204 - minimal +- `__add__` [method] L329 - minimal +- `__radd__` [method] L373 - minimal +- `__sub__` [method] L377 - minimal +- `__rsub__` [method] L438 - minimal +- `__mul__` [method] L470 - minimal +- `__rmul__` [method] L593 - minimal +- `__truediv__` [method] L597 - minimal +- `__rtruediv__` [method] L673 - partial +- `__pow__` [method] L710 - minimal +- `__neg__` [method] L718 - minimal +- `_sympy_` [method] L772 - partial +- `__float__` [method] L796 - minimal +- `__str__` [method] L808 - minimal +- `__repr__` [method] L814 - minimal +- `__format__` [method] L820 - minimal +- `_repr_latex_` [method] L835 - minimal +- `_repr_mimebundle_` [method] L856 - minimal +- `_ipython_display_` [method] L867 - minimal
@@ -1816,64 +2365,97 @@ Generated by `docstring_sweep.py` +### `src/underworld3/meshing/bounding_surface.py` + +**Public API:** + +- 📝 `BoundingSurface` [class] L41 + - NEEDS_RETURNS +- ❌ `mesh` (BoundingSurface) [property] L114 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- 📝 `normals` (BoundingSurface) [method] L118 + - NEEDS_PARAMETERS +- 📝 `tangent_project` (BoundingSurface) [method] L147 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `restore` (BoundingSurface) [method] L161 + - NEEDS_PARAMETERS +- 📝 `release` (BoundingSurface) [method] L193 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `register_radial_surfaces` [function] L213 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `register_plane_surfaces` [function] L224 + - NEEDS_PARAMETERS +- 📝 `register_box_face_surfaces` [function] L233 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +
+Private/Internal (4 items) + +- `__init__` [method] L63 - none +- `_unit` [function] L25 - none +- `__repr__` [method] L202 - none +- `_as_float` [function] L31 - minimal + +
+ ### `src/underworld3/meshing/faults.py` **Public API:** - 📝 `FaultSurface` [class] L69 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `FaultCollection` [class] L376 +- 📝 `FaultCollection` [class] L374 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `points` (FaultSurface) [property] L116 +- ⚠️ `points` (FaultSurface) [property] L114 - NEEDS_PARAMETERS -- ⚠️ `points` (FaultSurface) [method] L121 +- ⚠️ `points` (FaultSurface) [method] L119 - NEEDS_PARAMETERS -- ⚠️ `triangles` (FaultSurface) [property] L137 +- ⚠️ `triangles` (FaultSurface) [property] L135 - NEEDS_PARAMETERS -- ⚠️ `normals` (FaultSurface) [property] L142 +- ⚠️ `normals` (FaultSurface) [property] L140 - NEEDS_PARAMETERS -- ⚠️ `pv_mesh` (FaultSurface) [property] L147 +- ⚠️ `pv_mesh` (FaultSurface) [property] L145 - NEEDS_PARAMETERS -- ⚠️ `is_triangulated` (FaultSurface) [property] L152 +- ⚠️ `is_triangulated` (FaultSurface) [property] L150 - NEEDS_PARAMETERS -- ⚠️ `n_points` (FaultSurface) [property] L157 +- ⚠️ `n_points` (FaultSurface) [property] L155 - NEEDS_PARAMETERS -- ⚠️ `n_triangles` (FaultSurface) [property] L162 +- ⚠️ `n_triangles` (FaultSurface) [property] L160 - NEEDS_PARAMETERS -- ✅ `from_vtk` (FaultSurface) [method] L167 -- 📝 `triangulate` (FaultSurface) [method] L215 +- ✅ `from_vtk` (FaultSurface) [method] L165 +- 📝 `triangulate` (FaultSurface) [method] L213 - NEEDS_RETURNS -- 📝 `compute_normals` (FaultSurface) [method] L273 +- 📝 `compute_normals` (FaultSurface) [method] L271 - NEEDS_RETURNS -- ⚠️ `flip_normals` (FaultSurface) [method] L292 +- ⚠️ `flip_normals` (FaultSurface) [method] L290 - NEEDS_PARAMETERS -- 📝 `to_vtk` (FaultSurface) [method] L299 +- 📝 `to_vtk` (FaultSurface) [method] L297 - NEEDS_RETURNS -- 📝 `build_kdtree` (FaultSurface) [method] L328 +- 📝 `build_kdtree` (FaultSurface) [method] L326 - NEEDS_PARAMETERS -- ⚠️ `face_centers` (FaultSurface) [property] L350 +- ⚠️ `face_centers` (FaultSurface) [property] L348 - NEEDS_PARAMETERS -- 📝 `add` (FaultCollection) [method] L407 +- 📝 `add` (FaultCollection) [method] L405 - NEEDS_RETURNS -- ✅ `add_from_vtk` (FaultCollection) [method] L423 -- ✅ `remove` (FaultCollection) [method] L437 -- ⚠️ `names` (FaultCollection) [property] L464 +- ✅ `add_from_vtk` (FaultCollection) [method] L421 +- ✅ `remove` (FaultCollection) [method] L435 +- ⚠️ `names` (FaultCollection) [property] L462 - NEEDS_PARAMETERS -- ✅ `compute_distance_field` (FaultCollection) [method] L468 -- ✅ `transfer_normals` (FaultCollection) [method] L535 -- ✅ `create_weakness_function` (FaultCollection) [method] L606 +- ✅ `compute_distance_field` (FaultCollection) [method] L466 +- ✅ `transfer_normals` (FaultCollection) [method] L542 +- ✅ `create_weakness_function` (FaultCollection) [method] L622
Private/Internal (8 items) - `__init__` [method] L96 - partial -- `__init__` [method] L403 - minimal -- `__repr__` [method] L366 - none -- `__repr__` [method] L644 - none +- `__init__` [method] L401 - minimal +- `__repr__` [method] L364 - none +- `__repr__` [method] L660 - none - `_require_pyvista` [function] L56 - minimal -- `__getitem__` [method] L451 - minimal -- `__iter__` [method] L455 - minimal -- `__len__` [method] L459 - minimal +- `__getitem__` [method] L449 - minimal +- `__iter__` [method] L453 - minimal +- `__len__` [method] L457 - minimal
@@ -1881,107 +2463,129 @@ Generated by `docstring_sweep.py` **Public API:** -- 📝 `SurfaceVariable` [class] L71 +- 📝 `SurfaceVariable` [class] L367 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `Surface` [class] L317 +- 📝 `Surface` [class] L611 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `SurfaceCollection` [class] L1251 +- 📝 `SurfaceCollection` [class] L1951 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `units` (SurfaceVariable) [property] L142 +- ⚠️ `units` (SurfaceVariable) [property] L438 - NEEDS_PARAMETERS -- ⚠️ `has_units` (SurfaceVariable) [property] L147 +- ⚠️ `has_units` (SurfaceVariable) [property] L443 - NEEDS_PARAMETERS -- 📝 `data` (SurfaceVariable) [property] L152 +- 📝 `data` (SurfaceVariable) [property] L448 - NEEDS_PARAMETERS -- 📝 `data` (SurfaceVariable) [method] L185 +- 📝 `data` (SurfaceVariable) [method] L481 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `mark_stale` (SurfaceVariable) [method] L198 +- ⚠️ `mark_stale` (SurfaceVariable) [method] L494 - NEEDS_PARAMETERS -- 📝 `mask` (SurfaceVariable) [property] L203 +- 📝 `mask` (SurfaceVariable) [property] L499 - NEEDS_PARAMETERS -- 📝 `sym` (SurfaceVariable) [property] L234 +- 📝 `sym` (SurfaceVariable) [property] L530 - NEEDS_PARAMETERS -- ⚠️ `dim` (Surface) [property] L413 +- ⚠️ `dim` (Surface) [property] L720 - NEEDS_PARAMETERS -- ⚠️ `is_2d` (Surface) [property] L438 +- ⚠️ `is_2d` (Surface) [property] L745 - NEEDS_PARAMETERS -- 📝 `symbol` (Surface) [property] L443 +- 📝 `symbol` (Surface) [property] L750 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `symbol` (Surface) [method] L452 +- ⚠️ `symbol` (Surface) [method] L759 - NEEDS_PARAMETERS -- ⚠️ `control_points` (Surface) [property] L462 +- 📝 `control_points` (Surface) [property] L796 - NEEDS_PARAMETERS -- 📝 `set_control_points` (Surface) [method] L466 +- 📝 `set_control_points` (Surface) [method] L807 - NEEDS_RETURNS -- ⚠️ `vertices` (Surface) [property] L502 +- 📝 `vertices` (Surface) [property] L861 - NEEDS_PARAMETERS -- ⚠️ `n_vertices` (Surface) [property] L509 +- ⚠️ `n_vertices` (Surface) [property] L873 - NEEDS_PARAMETERS -- ⚠️ `n_triangles` (Surface) [property] L516 +- ⚠️ `n_triangles` (Surface) [property] L880 - NEEDS_PARAMETERS -- ⚠️ `normals` (Surface) [property] L523 +- ⚠️ `normals` (Surface) [property] L887 - NEEDS_PARAMETERS -- ⚠️ `triangles` (Surface) [property] L531 +- ⚠️ `triangles` (Surface) [property] L895 - NEEDS_PARAMETERS -- ⚠️ `face_centers` (Surface) [property] L541 +- ⚠️ `face_centers` (Surface) [property] L905 - NEEDS_PARAMETERS -- ⚠️ `face_normals` (Surface) [property] L549 +- ⚠️ `face_normals` (Surface) [property] L913 - NEEDS_PARAMETERS -- ⚠️ `pv_mesh` (Surface) [property] L557 +- 📝 `pv_mesh` (Surface) [property] L921 - NEEDS_PARAMETERS -- ⚠️ `is_discretized` (Surface) [property] L562 +- ⚠️ `is_discretized` (Surface) [property] L939 - NEEDS_PARAMETERS -- 📝 `discretize` (Surface) [method] L573 +- 📝 `discretize` (Surface) [method] L950 - NEEDS_RETURNS -- 📝 `deform_vertices` (Surface) [method] L695 +- 📝 `deform_vertices` (Surface) [method] L1071 - NEEDS_RETURNS -- 📝 `distance` (Surface) [property] L728 +- 📝 `distance` (Surface) [property] L1104 - NEEDS_PARAMETERS -- ✅ `influence_function` (Surface) [method] L824 -- ✅ `add_variable` (Surface) [method] L903 -- ✅ `get_variable` (Surface) [method] L950 -- ⚠️ `variables` (Surface) [property] L965 +- 📝 `abs_distance` (Surface) [property] L1142 - NEEDS_PARAMETERS -- 📝 `save` (Surface) [method] L971 +- ✅ `influence_function` (Surface) [method] L1254 +- ✅ `add_variable` (Surface) [method] L1342 +- ✅ `get_variable` (Surface) [method] L1389 +- ⚠️ `variables` (Surface) [property] L1404 + - NEEDS_PARAMETERS +- 📝 `save` (Surface) [method] L1410 - NEEDS_RETURNS -- ✅ `refinement_metric` (Surface) [method] L1012 -- ✅ `from_vtk` (Surface) [method] L1139 -- 📝 `compute_normals` (Surface) [method] L1193 +- ✅ `refinement_metric` (Surface) [method] L1451 +- ✅ `from_trace` (Surface) [method] L1580 +- ✅ `from_vtk` (Surface) [method] L1839 +- 📝 `compute_normals` (Surface) [method] L1893 - NEEDS_RETURNS -- 📝 `flip_normals` (Surface) [method] L1207 +- 📝 `flip_normals` (Surface) [method] L1907 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `add` (SurfaceCollection) [method] L1281 +- 📝 `add` (SurfaceCollection) [method] L1981 - NEEDS_RETURNS -- ✅ `add_from_vtk` (SurfaceCollection) [method] L1298 -- ✅ `remove` (SurfaceCollection) [method] L1318 -- ⚠️ `names` (SurfaceCollection) [property] L1347 - - NEEDS_PARAMETERS -- ✅ `compute_distance_field` (SurfaceCollection) [method] L1351 -- ✅ `transfer_normals` (SurfaceCollection) [method] L1414 -- ✅ `influence_function` (SurfaceCollection) [method] L1477 -- ✅ `create_weakness_function` (SurfaceCollection) [method] L1519 +- ✅ `add_from_vtk` (SurfaceCollection) [method] L1998 +- ✅ `remove` (SurfaceCollection) [method] L2018 +- ⚠️ `names` (SurfaceCollection) [property] L2047 + - NEEDS_PARAMETERS +- ✅ `compute_distance_field` (SurfaceCollection) [method] L2051 +- ✅ `transfer_normals` (SurfaceCollection) [method] L2111 +- ✅ `influence_function` (SurfaceCollection) [method] L2174 +- ✅ `refinement_metric` (SurfaceCollection) [method] L2214 +- ✅ `compute_nearest_fields` (SurfaceCollection) [method] L2266 +- ✅ `create_weakness_function` (SurfaceCollection) [method] L2381 +- ✅ `fault_metric_tensor` [function] L2484 +- ✅ `fault_comb_metric` [function] L2596 +- ✅ `compose_metrics` [function] L2711 +- ✅ `fault_metric` [function] L2836
-Private/Internal (18 items) +Private/Internal (31 items) -- `__init__` [method] L98 - partial -- `__init__` [method] L355 - partial -- `__init__` [method] L1275 - minimal -- `__repr__` [method] L301 - none -- `__repr__` [method] L1237 - none -- `__repr__` [method] L1550 - none -- `_require_pyvista` [function] L58 - minimal -- `_create_proxy` [method] L254 - minimal -- `_interpolate_to_proxy` [method] L263 - partial -- `_mark_all_proxies_stale` [method] L496 - minimal -- `_ensure_discretized` [method] L568 - minimal -- `_discretize_3d` [method] L601 - minimal -- `_discretize_2d` [method] L633 - minimal -- `_compute_distance_field` [method] L765 - partial -- `_on_mesh_adapted` [method] L991 - partial -- `__getitem__` [method] L1334 - minimal -- `__iter__` [method] L1338 - minimal -- `__len__` [method] L1342 - minimal +- `__init__` [method] L394 - partial +- `__init__` [method] L649 - partial +- `__init__` [method] L1975 - minimal +- `__repr__` [method] L595 - none +- `__repr__` [method] L1937 - none +- `__repr__` [method] L2412 - none +- `_to_nd_length` [function] L58 - complete +- `_require_pyvista` [function] L100 - minimal +- `_depth_to_km` [function] L113 - partial +- `_order_polyline` [function] L132 - complete +- `_interpolate_trace` [function] L186 - complete +- `_profile_to_edge_lengths` [function] L250 - complete +- `_compute_trace_perpendicular` [function] L294 - complete +- `_create_proxy` [method] L550 - minimal +- `_interpolate_to_proxy` [method] L559 - partial +- `_dimensionalise_coords` [method] L769 - partial +- `_mark_all_proxies_stale` [method] L855 - minimal +- `_ensure_discretized` [method] L945 - minimal +- `_discretize_3d` [method] L977 - minimal +- `_discretize_2d` [method] L1009 - minimal +- `_compute_distance_field` [method] L1172 - partial +- `_on_mesh_adapted` [method] L1430 - partial +- `__getitem__` [method] L2034 - minimal +- `__iter__` [method] L2038 - minimal +- `__len__` [method] L2042 - minimal +- `_fault_seg_distance_sym` [function] L2423 - partial +- `_fault_collect_polylines` [function] L2440 - partial +- `_fault_collect_segments` [function] L2477 - partial +- `_mesh_h0` [function] L2779 - partial +- `_fault_min_distance_np` [function] L2798 - minimal +- `_fault_mmg_metric` [function] L2815 - partial
@@ -1993,137 +2597,144 @@ Generated by `docstring_sweep.py` - NEEDS_PARAMETERS - 📝 `Model` [class] L57 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `ThermalConvectionConfig` [class] L4549 +- 📝 `ThermalConvectionConfig` [class] L4726 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `mesh` (Model) [property] L149 +- ⚠️ `mesh` (Model) [property] L181 - NEEDS_PARAMETERS -- ⚠️ `list_meshes` (Model) [method] L174 +- ⚠️ `list_meshes` (Model) [method] L206 - NEEDS_PARAMETERS -- ⚠️ `get_mesh` (Model) [method] L178 +- ⚠️ `get_mesh` (Model) [method] L210 - NEEDS_PARAMETERS -- 📝 `set_primary_mesh` (Model) [method] L182 +- 📝 `set_primary_mesh` (Model) [method] L214 - NEEDS_RETURNS -- 📝 `set_mesh` (Model) [method] L325 +- 📝 `set_mesh` (Model) [method] L384 - NEEDS_RETURNS -- ✅ `get_variable` (Model) [method] L389 -- ✅ `get_qualified_name` (Model) [method] L443 -- ✅ `transfer_variable_data` (Model) [method] L465 -- ⚠️ `list_variables` (Model) [method] L514 +- ✅ `get_variable` (Model) [method] L448 +- ✅ `get_qualified_name` (Model) [method] L502 +- ✅ `transfer_variable_data` (Model) [method] L524 +- ⚠️ `list_variables` (Model) [method] L573 - NEEDS_PARAMETERS -- ⚠️ `list_swarms` (Model) [method] L518 +- ⚠️ `list_swarms` (Model) [method] L577 - NEEDS_PARAMETERS -- ⚠️ `add_solver` (Model) [method] L522 +- ⚠️ `add_solver` (Model) [method] L581 - NEEDS_PARAMETERS -- ⚠️ `get_solver` (Model) [method] L527 +- ⚠️ `get_solver` (Model) [method] L586 - NEEDS_PARAMETERS -- 📝 `define_parameter` (Model) [method] L531 +- 📝 `tracker` (Model) [property] L591 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `save_state` (Model) [method] L613 +- 📝 `load_state` (Model) [method] L654 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `define_parameter` (Model) [method] L686 - NEEDS_RETURNS -- 📝 `set_material` (Model) [method] L551 +- 📝 `set_material` (Model) [method] L706 - NEEDS_RETURNS -- ⚠️ `get_material` (Model) [method] L569 +- ⚠️ `get_material` (Model) [method] L724 - NEEDS_PARAMETERS -- 📝 `set_reference_quantities` (Model) [method] L573 +- 📝 `set_reference_quantities` (Model) [method] L728 - NEEDS_RETURNS -- ⚠️ `get_reference_quantities` (Model) [method] L758 +- ⚠️ `get_reference_quantities` (Model) [method] L906 - NEEDS_PARAMETERS -- 📝 `fundamental_scales` (Model) [property] L763 +- 📝 `fundamental_scales` (Model) [property] L911 - NEEDS_PARAMETERS -- ⚠️ `has_units` (Model) [method] L809 +- ⚠️ `has_units` (Model) [method] L957 - NEEDS_PARAMETERS -- ✅ `validate_reference_quantities` (Model) [method] L813 -- 📝 `get_coordinate_unit` (Model) [method] L873 +- ✅ `validate_reference_quantities` (Model) [method] L961 +- 📝 `get_coordinate_unit` (Model) [method] L1021 - NEEDS_PARAMETERS -- 📝 `set_as_default` (Model) [method] L949 +- 📝 `set_as_default` (Model) [method] L1097 - NEEDS_PARAMETERS -- 📝 `get_unit_aliases` (Model) [method] L1306 +- 📝 `get_unit_aliases` (Model) [method] L1454 - NEEDS_PARAMETERS -- 📝 `derive_fundamental_scalings` (Model) [method] L1680 +- 📝 `derive_fundamental_scalings` (Model) [method] L1828 - NEEDS_PARAMETERS -- 📝 `get_fundamental_scales` (Model) [method] L1931 +- 📝 `get_fundamental_scales` (Model) [method] L2079 - NEEDS_PARAMETERS -- ✅ `get_scale_for_dimensionality` (Model) [method] L1978 -- 📝 `get_model_base_units` (Model) [method] L2094 +- ✅ `get_scale_for_dimensionality` (Model) [method] L2126 +- 📝 `get_model_base_units` (Model) [method] L2242 - NEEDS_PARAMETERS -- 📝 `get_scale_summary` (Model) [method] L2212 +- 📝 `get_scale_summary` (Model) [method] L2360 - NEEDS_PARAMETERS -- 📝 `list_derived_scales` (Model) [method] L2380 +- 📝 `list_derived_scales` (Model) [method] L2528 - NEEDS_PARAMETERS -- ✅ `validate_dimensional_completeness` (Model) [method] L2456 -- 📝 `set_scaling_mode` (Model) [method] L2674 +- ✅ `validate_dimensional_completeness` (Model) [method] L2604 +- 📝 `set_scaling_mode` (Model) [method] L2822 - NEEDS_RETURNS -- 📝 `get_scaling_mode` (Model) [method] L2707 +- 📝 `get_scaling_mode` (Model) [method] L2855 - NEEDS_PARAMETERS -- 📝 `show_optimal_units` (Model) [method] L2919 +- 📝 `show_optimal_units` (Model) [method] L3067 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ✅ `scale_to_physical` (Model) [method] L3250 -- ✅ `to_model_units` (Model) [method] L3311 -- 📝 `has_units_active` (Model) [method] L3563 +- ✅ `scale_to_physical` (Model) [method] L3398 +- ✅ `to_model_units` (Model) [method] L3459 +- 📝 `has_units_active` (Model) [method] L3711 - NEEDS_PARAMETERS -- ✅ `to_model_magnitude` (Model) [method] L3574 -- ✅ `from_model_magnitude` (Model) [method] L3693 -- 📝 `to_dict` (Model) [method] L3866 +- ✅ `to_model_magnitude` (Model) [method] L3722 +- ✅ `from_model_magnitude` (Model) [method] L3857 +- 📝 `to_dict` (Model) [method] L4043 - NEEDS_PARAMETERS -- ⚠️ `export_configuration` (Model) [method] L3910 +- ⚠️ `export_configuration` (Model) [method] L4087 - NEEDS_PARAMETERS -- 📝 `from_dict` (Model) [method] L3914 +- 📝 `from_dict` (Model) [method] L4091 - NEEDS_RETURNS -- ✅ `to_yaml` (Model) [method] L3955 -- 📝 `from_yaml` (Model) [method] L3978 +- ✅ `to_yaml` (Model) [method] L4132 +- 📝 `from_yaml` (Model) [method] L4155 - NEEDS_RETURNS -- ✅ `from_yaml_file` (Model) [method] L4001 -- ✅ `capture_petsc_state` (Model) [method] L4028 -- 📝 `restore_petsc_state` (Model) [method] L4087 +- ✅ `from_yaml_file` (Model) [method] L4178 +- ✅ `capture_petsc_state` (Model) [method] L4205 +- 📝 `restore_petsc_state` (Model) [method] L4264 - NEEDS_RETURNS -- 📝 `set_petsc_option` (Model) [method] L4116 +- 📝 `set_petsc_option` (Model) [method] L4293 - NEEDS_RETURNS -- 📝 `view` (Model) [method] L4143 +- 📝 `view` (Model) [method] L4320 - NEEDS_RETURNS -- 📝 `view` (Model) [method] L4339 +- 📝 `view` (Model) [method] L4516 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `get_default_model` [function] L4491 +- 📝 `get_default_model` [function] L4668 - NEEDS_PARAMETERS -- 📝 `reset_default_model` [function] L4517 +- 📝 `reset_default_model` [function] L4694 - NEEDS_PARAMETERS -- 📝 `to_petsc_options` (ThermalConvectionConfig) [method] L4586 +- 📝 `to_petsc_options` (ThermalConvectionConfig) [method] L4763 - NEEDS_PARAMETERS -- 📝 `to_materials_dict` (ThermalConvectionConfig) [method] L4621 +- 📝 `to_materials_dict` (ThermalConvectionConfig) [method] L4798 - NEEDS_PARAMETERS -- ⚠️ `create_model` [function] L4640 +- ⚠️ `create_model` [function] L4817 - NEEDS_PARAMETERS -- ✅ `create_thermal_convection_model` [function] L4645 +- ✅ `create_thermal_convection_model` [function] L4822
-Private/Internal (29 items) +Private/Internal (31 items) -- `__init__` [method] L119 - partial -- `_register_mesh` [method] L155 - partial -- `_register_swarm` [method] L200 - partial -- `_register_variable` [method] L213 - partial -- `_register_solver` [method] L311 - partial -- `_update_mesh_for_swarm` [method] L350 - partial -- `_lock_units` [method] L924 - partial -- `_check_units_locked` [method] L933 - partial -- `_simple_dimensional_analysis` [method] L972 - partial -- `_comprehensive_dimensional_analysis` [method] L981 - partial -- `_solve_available_dimensions` [method] L1037 - partial -- `_solve_complete_system` [method] L1124 - partial -- `_create_pint_registry` [method] L1178 - minimal -- `_substitute_display_aliases` [method] L1285 - partial -- `_round_scale_for_conditioning` [method] L1338 - complete -- `_round_to_nice_value` [method] L1413 - minimal -- `_generate_constant_name` [method] L1441 - minimal -- `_convert_to_user_time_unit` [method] L1483 - complete -- `_choose_display_units` [method] L1552 - complete -- `_format_scale_representations` [method] L2143 - complete -- `_get_domain_friendly_scale` [method] L2318 - minimal -- `_suggest_reference_quantities` [method] L2539 - complete -- `_handle_conversion_failure` [method] L2603 - complete -- `_optimize_scales_for_readability` [method] L2718 - complete -- `_find_nice_scale` [method] L2754 - complete -- `_detect_scaling_conflicts` [method] L2800 - complete -- `_convert_to_model_units_general` [method] L3375 - minimal -- `__repr__` [method] L4318 - minimal -- `__str__` [method] L4335 - minimal +- `__init__` [method] L144 - partial +- `_register_mesh` [method] L187 - partial +- `_register_swarm` [method] L232 - partial +- `_unregister_swarm` [method] L245 - partial +- `_register_variable` [method] L270 - partial +- `_register_solver` [method] L370 - partial +- `_update_mesh_for_swarm` [method] L409 - partial +- `_register_state_bearer` [method] L602 - partial +- `_lock_units` [method] L1072 - partial +- `_check_units_locked` [method] L1081 - partial +- `_simple_dimensional_analysis` [method] L1120 - partial +- `_comprehensive_dimensional_analysis` [method] L1129 - partial +- `_solve_available_dimensions` [method] L1185 - partial +- `_solve_complete_system` [method] L1272 - partial +- `_create_pint_registry` [method] L1326 - minimal +- `_substitute_display_aliases` [method] L1433 - partial +- `_round_scale_for_conditioning` [method] L1486 - complete +- `_round_to_nice_value` [method] L1561 - minimal +- `_generate_constant_name` [method] L1589 - minimal +- `_convert_to_user_time_unit` [method] L1631 - complete +- `_choose_display_units` [method] L1700 - complete +- `_format_scale_representations` [method] L2291 - complete +- `_get_domain_friendly_scale` [method] L2466 - minimal +- `_suggest_reference_quantities` [method] L2687 - complete +- `_handle_conversion_failure` [method] L2751 - complete +- `_optimize_scales_for_readability` [method] L2866 - complete +- `_find_nice_scale` [method] L2902 - complete +- `_detect_scaling_conflicts` [method] L2948 - complete +- `_convert_to_model_units_general` [method] L3523 - minimal +- `__repr__` [method] L4495 - minimal +- `__str__` [method] L4512 - minimal
@@ -2204,311 +2815,387 @@ Generated by `docstring_sweep.py` **Public API:** -- 📝 `SwarmType` [class] L52 +- 📝 `SwarmType` [class] L49 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `SwarmVariable` [class] L82 +- 📝 `SwarmVariable` [class] L75 - NEEDS_RETURNS -- 📝 `IndexSwarmVariable` [class] L1903 +- 📝 `IndexSwarmVariable` [class] L2146 - NEEDS_RETURNS -- 📝 `Swarm` [class] L2213 +- 📝 `Swarm` [class] L2603 - NEEDS_RETURNS -- 📝 `NodalPointSwarm` [class] L4301 +- 📝 `NodalPointSwarm` [class] L5076 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ❌ `write_proxy` (SwarmVariable) [method] L1827 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `read_timestep` (SwarmVariable) [method] L1838 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `sym_1d` (IndexSwarmVariable) [property] L2043 +- ❌ `write_proxy` (SwarmVariable) [method] L2032 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `visMask` (IndexSwarmVariable) [method] L2085 +- ❌ `read_timestep` (SwarmVariable) [method] L2043 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `clip_to_mesh` (Swarm) [property] L2818 +- ❌ `read_timestep` (Swarm) [method] L4137 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `clip_to_mesh` (Swarm) [method] L2822 +- ❌ `advection` (Swarm) [method] L4839 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `read_timestep` (Swarm) [method] L3474 +- ❌ `advection` (NodalPointSwarm) [method] L5191 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `advection` (Swarm) [method] L4002 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `advection` (NodalPointSwarm) [method] L4397 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ⚠️ `units` (SwarmVariable) [property] L360 +- ⚠️ `units` (SwarmVariable) [property] L366 - NEEDS_PARAMETERS -- ⚠️ `units` (SwarmVariable) [method] L365 +- ⚠️ `units` (SwarmVariable) [method] L371 - NEEDS_PARAMETERS -- ⚠️ `has_units` (SwarmVariable) [property] L374 +- ⚠️ `has_units` (SwarmVariable) [property] L380 - NEEDS_PARAMETERS -- ⚠️ `use_legacy_array` (SwarmVariable) [method] L936 +- ⚠️ `use_legacy_array` (SwarmVariable) [method] L987 - NEEDS_PARAMETERS -- ⚠️ `use_enhanced_array` (SwarmVariable) [method] L940 +- ⚠️ `use_enhanced_array` (SwarmVariable) [method] L991 - NEEDS_PARAMETERS -- 📝 `sync_disabled` (SwarmVariable) [method] L944 +- 📝 `sync_disabled` (SwarmVariable) [method] L995 - NEEDS_RETURNS -- 📝 `pack_uw_data_to_petsc` (SwarmVariable) [method] L1157 +- 📝 `pack_uw_data_to_petsc` (SwarmVariable) [method] L1285 - NEEDS_RETURNS -- 📝 `unpack_uw_data_from_petsc` (SwarmVariable) [method] L1220 +- 📝 `unpack_uw_data_from_petsc` (SwarmVariable) [method] L1344 - NEEDS_RETURNS -- 📝 `pack_raw_data_to_petsc` (SwarmVariable) [method] L1261 +- 📝 `pack_raw_data_to_petsc` (SwarmVariable) [method] L1381 - NEEDS_RETURNS -- ✅ `unpack_raw_data_from_petsc` (SwarmVariable) [method] L1306 -- ✅ `rbf_interpolate` (SwarmVariable) [method] L1380 -- ⚠️ `swarm` (SwarmVariable) [property] L1440 +- ✅ `unpack_raw_data_from_petsc` (SwarmVariable) [method] L1422 +- ✅ `rbf_interpolate` (SwarmVariable) [method] L1492 +- ⚠️ `swarm` (SwarmVariable) [property] L1554 - NEEDS_PARAMETERS -- ⚠️ `old_data` (SwarmVariable) [property] L1457 +- ⚠️ `old_data` (SwarmVariable) [property] L1571 - NEEDS_PARAMETERS -- 📝 `data` (SwarmVariable) [property] L1464 +- 📝 `data` (SwarmVariable) [property] L1578 - NEEDS_PARAMETERS -- 📝 `array` (SwarmVariable) [property] L1486 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `array` (SwarmVariable) [method] L1497 +- 📝 `array` (SwarmVariable) [property] L1625 + - NEEDS_PARAMETERS +- ⚠️ `array` (SwarmVariable) [method] L1667 - NEEDS_PARAMETERS -- 📝 `sym` (SwarmVariable) [property] L1512 +- 📝 `sym` (SwarmVariable) [property] L1682 - NEEDS_PARAMETERS -- 📝 `sym_1d` (SwarmVariable) [property] L1533 +- 📝 `sym_1d` (SwarmVariable) [property] L1703 - NEEDS_PARAMETERS -- ✅ `global_max` (SwarmVariable) [method] L1554 -- ✅ `global_min` (SwarmVariable) [method] L1592 -- ✅ `global_sum` (SwarmVariable) [method] L1629 -- ✅ `global_norm` (SwarmVariable) [method] L1664 -- 📝 `global_size` (SwarmVariable) [method] L1697 +- ✅ `global_max` (SwarmVariable) [method] L1724 +- ✅ `global_min` (SwarmVariable) [method] L1762 +- ✅ `global_sum` (SwarmVariable) [method] L1799 +- ✅ `global_norm` (SwarmVariable) [method] L1834 +- 📝 `global_size` (SwarmVariable) [method] L1867 - NEEDS_PARAMETERS -- 📝 `save` (SwarmVariable) [method] L1726 +- 📝 `save` (SwarmVariable) [method] L1896 - NEEDS_RETURNS -- 📝 `sym` (IndexSwarmVariable) [property] L2029 +- 📝 `sym` (IndexSwarmVariable) [property] L2301 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `createMask` (IndexSwarmVariable) [method] L2051 +- 📝 `sym_1d` (IndexSwarmVariable) [property] L2313 - NEEDS_PARAMETERS -- ⚠️ `viewMask` (IndexSwarmVariable) [method] L2068 +- ✅ `createMask` (IndexSwarmVariable) [method] L2337 +- ✅ `viewMask` (IndexSwarmVariable) [method] L2385 +- 📝 `visMask` (IndexSwarmVariable) [method] L2420 - NEEDS_PARAMETERS -- ⚠️ `view` (IndexSwarmVariable) [method] L2088 +- ⚠️ `view` (IndexSwarmVariable) [method] L2445 - NEEDS_PARAMETERS -- ⚠️ `mesh` (Swarm) [property] L2425 +- ⚠️ `mesh` (Swarm) [property] L3009 - NEEDS_PARAMETERS -- 📝 `mesh` (Swarm) [method] L2433 +- 📝 `mesh` (Swarm) [method] L3017 - NEEDS_RETURNS -- 📝 `local_size` (Swarm) [property] L2508 +- 📝 `local_size` (Swarm) [property] L3092 - NEEDS_PARAMETERS -- 📝 `data` (Swarm) [property] L2525 +- 📝 `data` (Swarm) [property] L3109 - NEEDS_PARAMETERS -- 📝 `points` (Swarm) [property] L2539 +- 📝 `points` (Swarm) [property] L3123 - NEEDS_PARAMETERS -- 📝 `points` (Swarm) [method] L2650 +- 📝 `points` (Swarm) [method] L3234 - NEEDS_RETURNS -- 📝 `coords` (Swarm) [property] L2709 +- 📝 `coords` (Swarm) [property] L3293 - NEEDS_PARAMETERS -- 📝 `coords` (Swarm) [method] L2751 +- 📝 `coords` (Swarm) [method] L3343 - NEEDS_RETURNS -- 📝 `units` (Swarm) [property] L2775 +- 📝 `units` (Swarm) [property] L3367 - NEEDS_PARAMETERS -- 📝 `dont_clip_to_mesh` (Swarm) [method] L2825 +- 📝 `clip_to_mesh` (Swarm) [property] L3410 + - NEEDS_PARAMETERS +- ⚠️ `clip_to_mesh` (Swarm) [method] L3431 + - NEEDS_PARAMETERS +- 📝 `dont_clip_to_mesh` (Swarm) [method] L3435 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `migration_disabled` (Swarm) [method] L2853 +- 📝 `migration_disabled` (Swarm) [method] L3463 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `migration_control` (Swarm) [method] L2871 +- 📝 `migration_control` (Swarm) [method] L3483 - NEEDS_RETURNS -- 📝 `populate` (Swarm) [method] L2923 +- 📝 `populate` (Swarm) [method] L3549 - NEEDS_RETURNS -- 📝 `migrate` (Swarm) [method] L3022 +- 📝 `migrate` (Swarm) [method] L3615 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ✅ `add_particles_with_coordinates` (Swarm) [method] L3194 -- ✅ `add_particles_with_global_coordinates` (Swarm) [method] L3269 -- 📝 `save` (Swarm) [method] L3347 +- ✅ `add_particles_with_coordinates` (Swarm) [method] L3811 +- ✅ `add_particles_with_global_coordinates` (Swarm) [method] L3885 +- 📝 `save` (Swarm) [method] L3982 - NEEDS_RETURNS -- ✅ `add_variable` (Swarm) [method] L3498 -- 📝 `petsc_save_checkpoint` (Swarm) [method] L3575 +- ✅ `add_variable` (Swarm) [method] L4176 +- 📝 `petsc_save_checkpoint` (Swarm) [method] L4253 - NEEDS_RETURNS -- 📝 `write_timestep` (Swarm) [method] L3599 +- 📝 `write_timestep` (Swarm) [method] L4277 - NEEDS_RETURNS -- 📝 `vars` (Swarm) [property] L3749 +- 📝 `vars` (Swarm) [property] L4427 - NEEDS_PARAMETERS -- ✅ `access` (Swarm) [method] L3900 -- ⚠️ `estimate_dt` (Swarm) [method] L4249 +- 📝 `snapshot_payload` (Swarm) [method] L4450 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `apply_snapshot_payload` (Swarm) [method] L4480 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `access` (Swarm) [method] L4738 +- ⚠️ `estimate_dt` (Swarm) [method] L5015 - NEEDS_PARAMETERS
-Private/Internal (28 items) - -- `__init__` [method] L151 - none -- `__init__` [method] L1943 - none -- `__init__` [method] L2308 - none -- `__init__` [method] L4310 - none -- `_data_layout` [method] L958 - none -- `_create_proxy_variable` [method] L1005 - none -- `__getitem__` [method] L2048 - none -- `_particle_coordinates` [property] L2705 - none -- `_data_layout` [method] L3952 - none -- `_get_map` [method] L3981 - none -- `_create_variable_array` [method] L378 - complete -- `_create_canonical_data_array` [method] L422 - complete -- `_create_array_view` [method] L490 - partial -- `_is_simple_variable` [method] L508 - minimal -- `_create_simple_array_view` [method] L512 - minimal -- `_create_tensor_array_view` [method] L722 - minimal -- `_pack_array_to_data_format` [method] L929 - minimal -- `_update` [method] L1021 - minimal -- `_update_proxy_if_stale` [method] L1036 - minimal -- `_rbf_to_meshVar` [method] L1060 - partial -- `_rbf_reduce_to_meshVar` [method] L1091 - partial -- `_object_viewer` [method] L1358 - minimal -- `_update` [method] L2010 - minimal -- `_on_data_changed` [method] L2018 - partial -- `_update_proxy_variables` [method] L2095 - partial -- `__del__` [method] L2415 - minimal -- `_force_migration_after_mesh_change` [method] L3160 - partial -- `_legacy_access` [method] L3759 - partial +Private/Internal (36 items) + +- `__init__` [method] L137 - none +- `__init__` [method] L2181 - none +- `__init__` [method] L2659 - none +- `__init__` [method] L5091 - none +- `_data_layout` [method] L1009 - none +- `_create_proxy_variable` [method] L1056 - none +- `__getitem__` [method] L2334 - none +- `_particle_coordinates` [property] L3289 - none +- `_data_layout` [method] L4790 - none +- `_get_map` [method] L4819 - none +- `_create_variable_array` [method] L384 - complete +- `_create_canonical_data_array` [method] L441 - complete +- `_create_array_view` [method] L526 - partial +- `_is_simple_variable` [method] L544 - minimal +- `_create_simple_array_view` [method] L548 - minimal +- `_create_tensor_array_view` [method] L761 - minimal +- `_pack_array_to_data_format` [method] L972 - minimal +- `_update` [method] L1086 - minimal +- `_update_proxy_if_stale` [method] L1106 - minimal +- `_rbf_to_meshVar` [method] L1152 - partial +- `_rbf_reduce_to_meshVar` [method] L1208 - partial +- `_warn_deprecated_sync` [method] L1275 - minimal +- `_object_viewer` [method] L1470 - minimal +- `_update` [method] L2248 - minimal +- `_on_data_changed` [method] L2256 - partial +- `_update_proxy_if_stale` [method] L2265 - partial +- `_update_proxy_variables` [method] L2452 - partial +- `__del__` [method] L2804 - partial +- `_invalidate_canonical_data` [method] L2839 - partial +- `_flush_pending_petsc_sync` [method] L2868 - partial +- `_sync_before_assembly` [method] L2901 - partial +- `_get_kdtree` [method] L2962 - minimal +- `_route_by_nearest_centroid` [method] L2975 - partial +- `_force_migration_after_mesh_change` [method] L3777 - partial +- `_snapshot_stable_name` [method] L4446 - minimal +- `_legacy_access` [method] L4597 - partial
-### `src/underworld3/swarms/pic_swarm.py` +### `src/underworld3/systems/ddt.py` **Public API:** -- 📝 `SwarmPICLayout` [class] L25 +- 📝 `DDtSymbolicState` [class] L105 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `PICSwarm` [class] L39 - - NEEDS_RETURNS -- 📝 `NodalPointPICSwarm` [class] L1387 +- 📝 `DDtEulerianState` [class] L116 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `mesh` (PICSwarm) [property] L232 - - NEEDS_PARAMETERS -- 📝 `data` (PICSwarm) [property] L243 - - NEEDS_PARAMETERS -- 📝 `particle_coordinates` (PICSwarm) [property] L256 - - NEEDS_PARAMETERS -- 📝 `particle_cellid` (PICSwarm) [property] L267 - - NEEDS_PARAMETERS -- 📝 `populate_petsc` (PICSwarm) [method] L281 - - NEEDS_RETURNS -- 📝 `populate` (PICSwarm) [method] L340 +- 📝 `DDtSemiLagrangianState` [class] L129 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `DDtLagrangianState` [class] L143 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `DDtLagrangianSwarmState` [class] L155 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `Symbolic` [class] L463 - NEEDS_RETURNS -- ✅ `add_particles_with_coordinates` (PICSwarm) [method] L436 -- 📝 `save` (PICSwarm) [method] L511 +- 📝 `Eulerian` [class] L807 - NEEDS_RETURNS -- 📝 `read_timestep` (PICSwarm) [method] L610 +- 📝 `SemiLagrangian` [class] L1309 - NEEDS_RETURNS -- ✅ `add_variable` (PICSwarm) [method] L646 -- 📝 `petsc_save_checkpoint` (PICSwarm) [method] L689 +- 📝 `Lagrangian` [class] L2920 - NEEDS_RETURNS -- 📝 `write_timestep` (PICSwarm) [method] L713 +- 📝 `Lagrangian_Swarm` [class] L3292 - NEEDS_RETURNS -- 📝 `vars` (PICSwarm) [property] L863 +- ❌ `state` (Eulerian) [method] L979 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (SemiLagrangian) [property] L1884 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (SemiLagrangian) [method] L1899 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (Lagrangian) [property] L3085 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (Lagrangian) [method] L3095 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (Lagrangian_Swarm) [property] L3442 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `state` (Lagrangian_Swarm) [method] L3452 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `state` (Symbolic) [property] L602 - NEEDS_PARAMETERS -- 📝 `access` (PICSwarm) [method] L873 - - NEEDS_RETURNS -- 📝 `advection` (PICSwarm) [method] L1069 - - NEEDS_RETURNS -- ⚠️ `estimate_dt` (PICSwarm) [method] L1334 +- ⚠️ `state` (Symbolic) [method] L613 - NEEDS_PARAMETERS -- 📝 `advection` (NodalPointPICSwarm) [method] L1476 - - NEEDS_RETURNS - -
-Private/Internal (4 items) - -- `__init__` [method] L143 - none -- `__init__` [method] L1396 - none -- `_data_layout` [method] L1018 - none -- `_get_map` [method] L1046 - none - -
- -### `src/underworld3/systems/ddt.py` - -**Public API:** - -- 📝 `Symbolic` [class] L50 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `Eulerian` [class] L240 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `SemiLagrangian` [class] L537 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `Lagrangian` [class] L1146 +- ⚠️ `psi_fn` (Symbolic) [property] L645 + - NEEDS_PARAMETERS +- ⚠️ `psi_fn` (Symbolic) [method] L650 + - NEEDS_PARAMETERS +- 📝 `effective_order` (Symbolic) [property] L672 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `Lagrangian_Swarm` [class] L1356 +- ⚠️ `update_history_fn` (Symbolic) [method] L683 + - NEEDS_PARAMETERS +- 📝 `initialise_history` (Symbolic) [method] L688 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `psi_fn` (Symbolic) [property] L104 +- ⚠️ `initiate_history_fn` (Symbolic) [method] L701 + - NEEDS_PARAMETERS +- ⚠️ `update` (Symbolic) [method] L705 - NEEDS_PARAMETERS -- ⚠️ `psi_fn` (Symbolic) [method] L109 +- ⚠️ `update_pre_solve` (Symbolic) [method] L715 - NEEDS_PARAMETERS -- ⚠️ `update_history_fn` (Symbolic) [method] L130 +- ⚠️ `update_post_solve` (Symbolic) [method] L733 - NEEDS_PARAMETERS -- ⚠️ `initiate_history_fn` (Symbolic) [method] L135 +- ⚠️ `bdf_coefficients` (Symbolic) [property] L761 - NEEDS_PARAMETERS -- ⚠️ `update` (Symbolic) [method] L143 +- ✅ `bdf` (Symbolic) [method] L765 +- ✅ `adams_moulton_flux` (Symbolic) [method] L781 +- 📝 `update_exp_coefficients` (Symbolic) [method] L794 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `state` (Eulerian) [property] L968 - NEEDS_PARAMETERS -- ⚠️ `update_pre_solve` (Symbolic) [method] L152 +- ⚠️ `psi_fn` (Eulerian) [property] L1007 - NEEDS_PARAMETERS -- ⚠️ `update_post_solve` (Symbolic) [method] L161 +- ⚠️ `psi_fn` (Eulerian) [method] L1012 - NEEDS_PARAMETERS -- ⚠️ `bdf` (Symbolic) [method] L176 +- 📝 `effective_order` (Eulerian) [property] L1094 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `update_history_fn` (Eulerian) [method] L1105 - NEEDS_PARAMETERS -- ✅ `adams_moulton_flux` (Symbolic) [method] L199 -- ⚠️ `psi_fn` (Eulerian) [property] L324 +- 📝 `initialise_history` (Eulerian) [method] L1123 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `set_initial_history` (Eulerian) [method] L1139 + - NEEDS_RETURNS +- ⚠️ `initiate_history_fn` (Eulerian) [method] L1182 - NEEDS_PARAMETERS -- ⚠️ `psi_fn` (Eulerian) [method] L329 +- ⚠️ `update` (Eulerian) [method] L1186 - NEEDS_PARAMETERS -- ⚠️ `update_history_fn` (Eulerian) [method] L379 +- 📝 `update_pre_solve` (Eulerian) [method] L1196 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `update_post_solve` (Eulerian) [method] L1244 - NEEDS_PARAMETERS -- ⚠️ `initiate_history_fn` (Eulerian) [method] L408 +- ⚠️ `bdf_coefficients` (Eulerian) [property] L1274 - NEEDS_PARAMETERS -- ⚠️ `update` (Eulerian) [method] L419 +- ✅ `bdf` (Eulerian) [method] L1278 +- ✅ `adams_moulton_flux` (Eulerian) [method] L1291 +- ⚠️ `update_exp_coefficients` (Eulerian) [method] L1304 - NEEDS_PARAMETERS -- ⚠️ `update_pre_solve` (Eulerian) [method] L428 +- 📝 `on_remesh` (SemiLagrangian) [method] L1804 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `psi_fn` (SemiLagrangian) [property] L1933 - NEEDS_PARAMETERS -- ⚠️ `update_post_solve` (Eulerian) [method] L436 +- 📝 `psi_fn` (SemiLagrangian) [method] L1938 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `enable_source_snapshot` (SemiLagrangian) [method] L1978 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `effective_order` (SemiLagrangian) [property] L2043 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `initialise_history` (SemiLagrangian) [method] L2054 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `set_initial_history` (SemiLagrangian) [method] L2101 + - NEEDS_RETURNS +- ⚠️ `initiate_history_fn` (SemiLagrangian) [method] L2144 - NEEDS_PARAMETERS -- ⚠️ `bdf` (Eulerian) [method] L474 +- ⚠️ `update` (SemiLagrangian) [method] L2249 - NEEDS_PARAMETERS -- ✅ `adams_moulton_flux` (Eulerian) [method] L500 -- ⚠️ `psi_fn` (SemiLagrangian) [property] L690 +- ⚠️ `update_post_solve` (SemiLagrangian) [method] L2260 - NEEDS_PARAMETERS -- ⚠️ `psi_fn` (SemiLagrangian) [method] L695 +- ✅ `update_pre_solve` (SemiLagrangian) [method] L2280 +- ⚠️ `bdf_coefficients` (SemiLagrangian) [property] L2765 - NEEDS_PARAMETERS -- ⚠️ `update` (SemiLagrangian) [method] L708 +- ✅ `bdf` (SemiLagrangian) [method] L2769 +- ✅ `adams_moulton_flux` (SemiLagrangian) [method] L2782 +- 📝 `update_exp_coefficients` (SemiLagrangian) [method] L2795 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `update_forcing_history` (SemiLagrangian) [method] L2815 +- 📝 `on_remesh` (Lagrangian) [method] L3072 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `effective_order` (Lagrangian) [property] L3134 - NEEDS_PARAMETERS -- ⚠️ `update_post_solve` (SemiLagrangian) [method] L719 +- 📝 `initialise_history` (Lagrangian) [method] L3140 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `initiate_history_fn` (Lagrangian) [method] L3169 - NEEDS_PARAMETERS -- ⚠️ `update_pre_solve` (SemiLagrangian) [method] L729 +- ⚠️ `update` (Lagrangian) [method] L3180 - NEEDS_PARAMETERS -- ⚠️ `bdf` (SemiLagrangian) [method] L1074 +- ⚠️ `update_pre_solve` (Lagrangian) [method] L3190 - NEEDS_PARAMETERS -- ✅ `adams_moulton_flux` (SemiLagrangian) [method] L1100 -- ⚠️ `update` (Lagrangian) [method] L1236 +- ⚠️ `update_post_solve` (Lagrangian) [method] L3208 - NEEDS_PARAMETERS -- ⚠️ `update_pre_solve` (Lagrangian) [method] L1246 +- ⚠️ `bdf_coefficients` (Lagrangian) [property] L3261 - NEEDS_PARAMETERS -- ⚠️ `update_post_solve` (Lagrangian) [method] L1255 +- ✅ `bdf` (Lagrangian) [method] L3265 +- ✅ `adams_moulton_flux` (Lagrangian) [method] L3278 +- ⚠️ `effective_order` (Lagrangian_Swarm) [property] L3491 - NEEDS_PARAMETERS -- ⚠️ `bdf` (Lagrangian) [method] L1292 +- 📝 `initialise_history` (Lagrangian_Swarm) [method] L3497 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `initiate_history_fn` (Lagrangian_Swarm) [method] L3526 - NEEDS_PARAMETERS -- ✅ `adams_moulton_flux` (Lagrangian) [method] L1319 -- ⚠️ `update` (Lagrangian_Swarm) [method] L1440 +- ⚠️ `update` (Lagrangian_Swarm) [method] L3537 - NEEDS_PARAMETERS -- ⚠️ `update_pre_solve` (Lagrangian_Swarm) [method] L1450 +- ⚠️ `update_pre_solve` (Lagrangian_Swarm) [method] L3547 - NEEDS_PARAMETERS -- ⚠️ `update_post_solve` (Lagrangian_Swarm) [method] L1459 +- ⚠️ `update_post_solve` (Lagrangian_Swarm) [method] L3565 - NEEDS_PARAMETERS -- ⚠️ `bdf` (Lagrangian_Swarm) [method] L1512 +- ⚠️ `bdf_coefficients` (Lagrangian_Swarm) [property] L3619 - NEEDS_PARAMETERS -- ✅ `adams_moulton_flux` (Lagrangian_Swarm) [method] L1541 +- ✅ `bdf` (Lagrangian_Swarm) [method] L3623 +- ✅ `adams_moulton_flux` (Lagrangian_Swarm) [method] L3636
-Private/Internal (11 items) +Private/Internal (26 items) -- `__init__` [method] L68 - none -- `__init__` [method] L254 - none -- `__init__` [method] L552 - none -- `__init__` [method] L1167 - none -- `__init__` [method] L1377 - none -- `_object_viewer` [method] L121 - none -- `_object_viewer` [method] L335 - none -- `_object_viewer` [method] L701 - none -- `_object_viewer` [method] L1214 - none -- `_object_viewer` [method] L1418 - none -- `_setup_projections` [method] L350 - minimal +- `_DDtCoreState` [class] L85 - partial +- `__init__` [method] L522 - none +- `__init__` [method] L872 - none +- `__init__` [method] L1444 - none +- `__init__` [method] L2988 - none +- `__init__` [method] L3375 - none +- `_object_viewer` [method] L662 - none +- `_object_viewer` [method] L1018 - none +- `_object_viewer` [method] L2035 - none +- `_object_viewer` [method] L3122 - none +- `_object_viewer` [method] L3479 - none +- `_as_float` [function] L166 - minimal +- `_bdf_coefficients` [function] L182 - complete +- `_create_coefficients` [function] L312 - complete +- `_update_bdf_values` [function] L341 - minimal +- `_update_am_values` [function] L353 - partial +- `_create_exp_coefficients` [function] L377 - partial +- `_update_exp_values` [function] L399 - partial +- `_build_weighted_sum` [function] L436 - complete +- `_setup_projections` [method] L1026 - minimal +- `_build_projection_source` [method] L1952 - partial +- `_activate_ale_for_traceback` [method] L2148 - partial +- `_consume_ale_pulse` [method] L2201 - partial +- `_record_psi_star_from_field_data` [method] L2213 - partial +- `_exp_alpha` [property] L2806 - minimal +- `_exp_phi` [property] L2811 - minimal
+### `src/underworld3/tests/test_adaptivity_metrics.py` + +**Public API:** + +- ⚠️ `TestCreateMetric` [class] L28 + - NEEDS_PARAMETERS +- ⚠️ `TestMetricFromGradient` [class] L58 + - NEEDS_PARAMETERS +- ⚠️ `TestMetricFromField` [class] L117 + - NEEDS_PARAMETERS +- ⚠️ `simple_mesh` [function] L18 + - NEEDS_PARAMETERS +- ⚠️ `test_basic_creation` (TestCreateMetric) [method] L31 + - NEEDS_PARAMETERS +- ⚠️ `test_variable_h_field` (TestCreateMetric) [method] L44 + - NEEDS_PARAMETERS +- ⚠️ `test_gaussian_gradient` (TestMetricFromGradient) [method] L61 + - NEEDS_PARAMETERS +- ⚠️ `test_uniform_field_uses_h_max` (TestMetricFromGradient) [method] L86 + - NEEDS_PARAMETERS +- ⚠️ `test_profiles` (TestMetricFromGradient) [method] L99 + - NEEDS_PARAMETERS +- ⚠️ `test_basic_mapping` (TestMetricFromField) [method] L120 + - NEEDS_PARAMETERS +- ⚠️ `test_invert_option` (TestMetricFromField) [method] L141 + - NEEDS_PARAMETERS + ### `src/underworld3/units.py` **Public API:** @@ -2530,38 +3217,38 @@ Generated by `docstring_sweep.py` - ✅ `check_units_consistency` [function] L510 - ✅ `get_dimensionality` [function] L567 - ✅ `get_units` [function] L591 -- ✅ `non_dimensionalise` [function] L645 -- ✅ `show_nondimensional_form` [function] L883 -- ✅ `dimensionalise` [function] L929 -- ✅ `simplify_units` [function] L1086 -- ✅ `create_quantity` [function] L1115 -- ✅ `convert_units` [function] L1138 -- ✅ `to_base_units` [function] L1272 -- ✅ `to_reduced_units` [function] L1366 -- ✅ `to_compact` [function] L1452 -- 📝 `get_scaling_coefficients` [function] L1542 - - NEEDS_PARAMETERS -- 📝 `set_scaling_coefficients` [function] L1560 +- ✅ `non_dimensionalise` [function] L643 +- ✅ `show_nondimensional_form` [function] L887 +- ✅ `dimensionalise` [function] L933 +- ✅ `simplify_units` [function] L1092 +- ✅ `create_quantity` [function] L1121 +- ✅ `convert_units` [function] L1155 +- ✅ `to_base_units` [function] L1289 +- ✅ `to_reduced_units` [function] L1383 +- ✅ `to_compact` [function] L1469 +- 📝 `get_scaling_coefficients` [function] L1559 + - NEEDS_PARAMETERS +- 📝 `set_scaling_coefficients` [function] L1577 - NEEDS_RETURNS -- ⚠️ `is_dimensionless` [function] L1578 +- ⚠️ `is_dimensionless` [function] L1595 - NEEDS_PARAMETERS -- ⚠️ `has_units` [function] L1583 +- ⚠️ `has_units` [function] L1600 - NEEDS_PARAMETERS -- ⚠️ `same_units` [function] L1588 +- ⚠️ `same_units` [function] L1605 - NEEDS_PARAMETERS -- ⚠️ `is_velocity` [function] L1596 +- ⚠️ `is_velocity` [function] L1613 - NEEDS_PARAMETERS -- ⚠️ `is_pressure` [function] L1606 +- ⚠️ `is_pressure` [function] L1623 - NEEDS_PARAMETERS -- ✅ `validate_expression_units` [function] L1616 -- 📝 `assert_dimensionality` [function] L1641 +- ✅ `validate_expression_units` [function] L1633 +- 📝 `assert_dimensionality` [function] L1658 - NEEDS_RETURNS -- 📝 `validate_coordinates_dimensionality` [function] L1772 +- 📝 `validate_coordinates_dimensionality` [function] L1789 - NEEDS_RETURNS -- 📝 `enforce_units_consistency` [function] L1827 +- 📝 `enforce_units_consistency` [function] L1844 - NEEDS_RETURNS -- ✅ `require_units_if_active` [function] L1847 -- ✅ `convert_angle_to_degrees` [function] L1947 +- ✅ `require_units_if_active` [function] L1864 +- ✅ `convert_angle_to_degrees` [function] L1964
Private/Internal (8 items) @@ -2577,177 +3264,814 @@ Generated by `docstring_sweep.py`
-### `src/underworld3/__init__.py` +### `src/underworld3/utilities/_interrupt.py` **Public API:** -- ❌ `view` [function] L94 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- 📝 `use_nondimensional_scaling` [function] L268 - - NEEDS_RETURNS -- 📝 `is_nondimensional_scaling_active` [function] L327 - - NEEDS_PARAMETERS -- ✅ `nondimensional_scaling_context` [function] L344 -- 📝 `use_strict_units` [function] L392 +- 📝 `UW_Pause` [class] L64 - NEEDS_RETURNS -- 📝 `is_strict_units_active` [function] L447 +- 📝 `is_notebook` [function] L25 - NEEDS_PARAMETERS -- 📝 `unwrap` [function] L493 +- 📝 `pause` [function] L137 - NEEDS_RETURNS -- ✅ `synchronised_array_update` [function] L542
-Private/Internal (3 items) +Private/Internal (4 items) -- `_units_view` [function] L201 - minimal -- `_apply_scaling` [function] L471 - minimal -- `_is_scaling_active` [function] L482 - minimal +- `__init__` [method] L99 - none +- `__repr__` [method] L131 - none +- `_render_traceback_` [method] L104 - partial +- `__str__` [method] L124 - minimal
-### `src/underworld3/adaptivity.py` +### `src/underworld3/utilities/_jitextension.py` **Public API:** -- ❌ `mesh_adapt_meshVar` [function] L508 +- 📝 `JITCallbackSet` [class] L194 + - NEEDS_RETURNS +- ❌ `debugging_text` [function] L490 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ✅ `create_metric` [function] L88 -- ✅ `metric_from_gradient` [function] L159 -- ✅ `metric_from_field` [function] L319 -- 📝 `mesh2mesh_swarm` [function] L549 +- ❌ `debugging_text_bd` [function] L512 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- 📝 `flat` (JITCallbackSet) [method] L230 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `signature` (JITCallbackSet) [method] L238 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `mesh2mesh_meshVariable` [function] L790 +- ⚠️ `map` (JITCallbackSet) [method] L246 - NEEDS_PARAMETERS +- ⚠️ `counts` (JITCallbackSet) [property] L257 + - NEEDS_PARAMETERS +- 📝 `prepare_for_cache_key` [function] L263 + - NEEDS_RETURNS +- ✅ `getext` [function] L538 +- ✅ `generate_c_source` [function] L748 +- ✅ `compile_and_load` [function] L1282
-Private/Internal (2 items) +Private/Internal (13 items) + +- `_JITConstant` [class] L303 - partial +- `__new__` [method] L311 - none +- `_ccode` [method] L319 - none +- `_stable_sort_key` [function] L14 - partial +- `_stable_sorted` [function] L38 - minimal +- `_petsc_build_env` [function] L44 - partial +- `_abi_salt` [function] L164 - partial +- `__post_init__` [method] L221 - minimal +- `_extract_constants` [function] L323 - complete +- `_is_truly_constant` [function] L383 - partial +- `_collect_constant_atoms` [function] L429 - minimal +- `_pack_constants` [function] L454 - complete +- `_createext` [function] L1377 - partial + +
+ +### `src/underworld3/utilities/_params.py` + +**Public API:** -- `_dm_stack_bcs` [function] L448 - none -- `_dm_unstack_bcs` [function] L472 - minimal +- ⚠️ `ParamType` [class] L51 + - NEEDS_PARAMETERS +- 📝 `Param` [class] L62 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `Params` [class] L116 +- 📝 `summary` (Params) [method] L423 + - NEEDS_RETURNS +- 📝 `to_dict` (Params) [method] L498 + - NEEDS_RETURNS +- 📝 `reset` (Params) [method] L502 + - NEEDS_RETURNS +- ⚠️ `cli_help` (Params) [method] L522 + - NEEDS_PARAMETERS + +
+Private/Internal (9 items) + +- `__init__` [method] L146 - partial +- `__post_init__` [method] L88 - none +- `_parse_cli_value` [method] L190 - partial +- `_validate_bounds` [method] L247 - minimal +- `_get_petsc_option` [method] L272 - minimal +- `__getattr__` [method] L315 - minimal +- `__setattr__` [method] L324 - minimal +- `__repr__` [method] L339 - partial +- `_repr_markdown_` [method] L379 - minimal
-### `src/underworld3/cython/petsc_discretisation.pyx` +### `src/underworld3/utilities/_utils.py` **Public API:** -- ❌ `petsc_fvm_get_min_radius` [method] L14 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_fvm_get_local_cell_sizes` [method] L35 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_dm_create_submesh_from_label` [method] L61 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_dm_find_labeled_points_local` [method] L85 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_dm_get_periodicity` [function] L150 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_dm_set_periodicity` [method] L184 +- 📝 `CaptureStdout` [class] L131 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ❌ `start` (CaptureStdout) [method] L164 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_vec_concatenate` [method] L221 +- ❌ `stop` (CaptureStdout) [method] L168 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `petsc_get_swarm_coord_name` [method] L244 +- ❌ `h5_scan` [function] L174 - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `get_installation_data` (_uw_record) [property] L98 + - NEEDS_PARAMETERS +- ⚠️ `get_runtime_data` (_uw_record) [property] L105 + - NEEDS_PARAMETERS +- ⚠️ `mem_footprint` [function] L186 + - NEEDS_PARAMETERS +- ✅ `gather_data` [function] L196 +- 📝 `postHog` [function] L259 + - NEEDS_RETURNS -### `src/underworld3/discretisation/discretisation_mesh_variables.py` +
+Private/Internal (5 items) + +- `_uw_record` [class] L9 - minimal +- `__init__` [method] L14 - none +- `__init__` [method] L142 - none +- `__enter__` [method] L150 - none +- `__exit__` [method] L155 - none + +
+ +### `src/underworld3/utilities/custom_mg.py` **Public API:** -- ❌ `extend_enum` [function] L61 - - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ❌ `load_from_h5_plex_vector` (_BaseMeshVariable) [method] L1217 +- 📝 `CustomMGHierarchy` [class] L509 + - NEEDS_RETURNS +- ❌ `install` (CustomMGHierarchy) [method] L596 - NEEDS_OVERVIEW, NEEDS_PARAMETERS -- ⚠️ `units` (_BaseMeshVariable) [property] L428 +- 📝 `barycentric_prolongation` [function] L51 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `rbf_prolongation` [function] L78 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `sbr_refine` [function] L154 + - NEEDS_PARAMETERS +- ⚠️ `sbr_refine_where` [function] L162 - NEEDS_PARAMETERS -- ⚠️ `units` (_BaseMeshVariable) [method] L433 +- 📝 `build` (CustomMGHierarchy) [method] L536 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `set_custom_fmg` [function] L605 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `inject_custom_mg` [function] L625 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +
+Private/Internal (16 items) + +- `__init__` [method] L525 - none +- `_to_petsc_aij` [function] L175 - none +- `_require_serial` [function] L107 - partial +- `_sbr_apply` [function] L127 - partial +- `_reduce_to_global` [function] L188 - partial +- `_field_subdm` [function] L221 - minimal +- `_reduced_map` [function] L234 - partial +- `_coarse_reduced_map` [function] L254 - partial +- `_reduced_transfer` [function] L275 - minimal +- `_level_dof_layout` [function] L293 - partial +- `_coarse_dof_layout` [function] L315 - minimal +- `_build_parallel_transfer` [function] L325 - partial +- `_assert_no_zero_columns_parallel` [function] L362 - partial +- `_configure_pcmg` [function] L378 - partial +- `_install_transfers` [function] L410 - partial +- `_install_velocity_block_transfers` [function] L448 - partial + +
+ +### `src/underworld3/utilities/diagnostics.py` + +**Public API:** + +- ⚠️ `Status` [class] L24 - NEEDS_PARAMETERS -- ⚠️ `has_units` (_BaseMeshVariable) [property] L438 +- ⚠️ `DiagnosticResult` [class] L33 - NEEDS_PARAMETERS -- ⚠️ `dimensionality` (_BaseMeshVariable) [property] L443 +- 📝 `check_petsc_library_match` [function] L118 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `check_petsc_version` [function] L235 - NEEDS_PARAMETERS -- ✅ `clone` (_BaseMeshVariable) [method] L609 -- 📝 `pack_raw_data_to_petsc` (_BaseMeshVariable) [method] L640 - - NEEDS_RETURNS -- 📝 `pack_uw_data_to_petsc` (_BaseMeshVariable) [method] L694 - - NEEDS_RETURNS -- ✅ `unpack_raw_data_from_petsc` (_BaseMeshVariable) [method] L797 -- ✅ `unpack_uw_data_from_petsc` (_BaseMeshVariable) [method] L846 -- ✅ `rbf_interpolate` (_BaseMeshVariable) [method] L905 -- 📝 `save` (_BaseMeshVariable) [method] L953 - - NEEDS_RETURNS -- 📝 `write` (_BaseMeshVariable) [method] L1031 - - NEEDS_RETURNS -- 📝 `read_timestep` (_BaseMeshVariable) [method] L1125 +- 📝 `check_petsc_version_match` [function] L265 - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `fn` (_BaseMeshVariable) [property] L1247 +- ⚠️ `check_extension_modules` [function] L385 - NEEDS_PARAMETERS -- ⚠️ `ijk` (_BaseMeshVariable) [property] L1254 +- ⚠️ `check_mpi_configuration` [function] L423 - NEEDS_PARAMETERS -- ⚠️ `sym` (_BaseMeshVariable) [property] L1261 +- ⚠️ `check_basic_evaluation` [function] L445 - NEEDS_PARAMETERS -- 📝 `sym_1d` (_BaseMeshVariable) [property] L1269 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- ⚠️ `mesh` (_BaseMeshVariable) [property] L1537 +- ⚠️ `check_environment_variables` [function] L514 - NEEDS_PARAMETERS -- ⚠️ `vec` (_BaseMeshVariable) [property] L1554 +- ✅ `run_diagnostics` [function] L568 +- ✅ `doctor` [function] L655 +- ⚠️ `health_check` [function] L684 - NEEDS_PARAMETERS -- 📝 `old_data` (_BaseMeshVariable) [property] L1568 - - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `array` (_BaseMeshVariable) [property] L1583 + +
+Private/Internal (2 items) + +- `_get_extension_library_paths` [function] L42 - minimal +- `_resolve_rpath` [function] L88 - minimal + +
+ +### `src/underworld3/utilities/dimensionality_mixin.py` + +**Public API:** + +- 📝 `DimensionalityMixin` [class] L13 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `data` (_BaseMeshVariable) [property] L2253 +- ⚠️ `NonDimensionalView` [class] L152 - NEEDS_PARAMETERS -- ⚠️ `array` (_BaseMeshVariable) [method] L2341 +- ⚠️ `dimensionality` (DimensionalityMixin) [property] L33 - NEEDS_PARAMETERS -- 📝 `min` (_BaseMeshVariable) [method] L2395 +- ⚠️ `scaling_coefficient` (DimensionalityMixin) [property] L57 - NEEDS_PARAMETERS -- 📝 `max` (_BaseMeshVariable) [method] L2423 +- ⚠️ `scaling_coefficient` (DimensionalityMixin) [method] L62 - NEEDS_PARAMETERS -- 📝 `sum` (_BaseMeshVariable) [method] L2451 +- ⚠️ `is_nondimensional` (DimensionalityMixin) [property] L82 - NEEDS_PARAMETERS -- 📝 `norm` (_BaseMeshVariable) [method] L2479 +- ⚠️ `nd_array` (DimensionalityMixin) [property] L87 - NEEDS_PARAMETERS -- 📝 `mean` (_BaseMeshVariable) [method] L2512 +- ✅ `from_nd` (DimensionalityMixin) [method] L99 +- 📝 `set_reference_scale` (DimensionalityMixin) [method] L111 + - NEEDS_RETURNS +- ⚠️ `array` (NonDimensionalView) [property] L164 - NEEDS_PARAMETERS -- 📝 `std` (_BaseMeshVariable) [method] L2539 +- ⚠️ `array` (NonDimensionalView) [method] L173 - NEEDS_PARAMETERS -- 📝 `stats` (_BaseMeshVariable) [method] L2595 +- ⚠️ `data` (NonDimensionalView) [property] L178 - NEEDS_PARAMETERS -- 📝 `coords` (_BaseMeshVariable) [property] L2761 +- ⚠️ `data` (NonDimensionalView) [method] L187 - NEEDS_PARAMETERS -- 📝 `coords_nd` (_BaseMeshVariable) [property] L2791 +- ⚠️ `sym` (NonDimensionalView) [property] L192 - NEEDS_PARAMETERS -- 📝 `divergence` (_BaseMeshVariable) [method] L2831 +- ⚠️ `dimensionality` (NonDimensionalView) [property] L205 - NEEDS_PARAMETERS -- 📝 `gradient` (_BaseMeshVariable) [method] L2848 +- ⚠️ `units` (NonDimensionalView) [property] L210 - NEEDS_PARAMETERS -- 📝 `curl` (_BaseMeshVariable) [method] L2863 +- ⚠️ `is_nondimensional` (NonDimensionalView) [property] L215 - NEEDS_PARAMETERS -- 📝 `jacobian` (_BaseMeshVariable) [method] L2878 +- ⚠️ `to_dimensional` (NonDimensionalView) [method] L219 - NEEDS_PARAMETERS
-Private/Internal (20 items) +Private/Internal (4 items) -- `_BaseMeshVariable` [class] L73 - partial -- `__init__` [method] L156 - partial -- `_data_layout` [method] L1299 - none -- `_setup_ds` [method] L1344 - none -- `_set_vec` [method] L1436 - none -- `__del__` [method] L1530 - none -- `__new__` [method] L102 - minimal -- `_create_variable_array` [method] L455 - complete -- `_create_flat_data_array` [method] L518 - complete -- `_object_viewer` [method] L580 - minimal -- `_replace_from_adapted_mesh` [method] L1448 - complete -- `_create_array_view` [method] L1593 - partial -- `_is_simple_variable` [method] L1611 - minimal -- `_create_simple_array_view` [method] L1615 - minimal -- `_create_tensor_array_view` [method] L1943 - minimal -- `_create_canonical_data_array` [method] L2274 - partial -- `_dimensionalise_stat` [method] L2350 - complete -- `_scalar_stats` [method] L2658 - minimal -- `_vector_stats` [method] L2682 - minimal -- `_tensor_stats` [method] L2720 - minimal +- `__init__` [method] L158 - none +- `__init__` [method] L24 - minimal +- `__repr__` [method] L223 - none +- `_create_nondimensional_expression` [method] L120 - minimal + +
+ +### `src/underworld3/utilities/docstring_utils.py` + +**Public API:** + +- ⚠️ `DocstringFormat` [class] L15 + - NEEDS_PARAMETERS +- ⚠️ `ParsedDocstring` [class] L23 + - NEEDS_PARAMETERS +- ✅ `detect_format` [function] L35 +- ✅ `parse_numpy_docstring` [function] L82 +- ✅ `parse_markdown_docstring` [function] L179 +- ✅ `numpy_to_markdown` [function] L232 +- ✅ `markdown_to_numpy` [function] L295 +- 📝 `in_jupyter` [function] L389 + - NEEDS_PARAMETERS +- ✅ `markdown_to_plain` [function] L409 +- ✅ `render_docstring` [function] L444 + +
+Private/Internal (3 items) + +- `_parse_parameter_section` [function] L139 - complete +- `_rst_math_to_latex` [function] L316 - complete +- `_latex_to_rst_math` [function] L358 - complete + +
+ +### `src/underworld3/utilities/mathematical_mixin.py` + +**Public API:** + +- 📝 `MathematicalMixin` [class] L18 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `UnitAwareDerivativeMatrix` [class] L802 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `diff` (MathematicalMixin) [method] L184 + - NEEDS_PARAMETERS +- ✅ `norm` (MathematicalMixin) [method] L601 +- 📝 `sym_repr` (MathematicalMixin) [method] L635 + - NEEDS_PARAMETERS +- ✅ `to` (MathematicalMixin) [method] L652 +- ⚠️ `has_units` (UnitAwareDerivativeMatrix) [property] L828 + - NEEDS_PARAMETERS +- ⚠️ `units` (UnitAwareDerivativeMatrix) [property] L833 + - NEEDS_PARAMETERS +- ⚠️ `dimensionality` (UnitAwareDerivativeMatrix) [property] L882 + - NEEDS_PARAMETERS +- ✅ `to` (UnitAwareDerivativeMatrix) [method] L894 +- ⚠️ `shape` (UnitAwareDerivativeMatrix) [property] L987 + - NEEDS_PARAMETERS +- 📝 `diff` (UnitAwareDerivativeMatrix) [method] L995 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `test_mathematical_mixin_fixed` [function] L1027 + - NEEDS_PARAMETERS + +
+Private/Internal (28 items) + +- `__init__` [method] L813 - none +- `__repr__` [method] L975 - none +- `_validate_sym` [method] L42 - minimal +- `_sympy_` [method] L67 - minimal +- `__getitem__` [method] L74 - minimal +- `__iter__` [method] L111 - partial +- `__len__` [method] L134 - minimal +- `__repr__` [method] L145 - minimal +- `_repr_latex_` [method] L154 - minimal +- `_ipython_display_` [method] L164 - minimal +- `__add__` [method] L239 - minimal +- `__radd__` [method] L287 - minimal +- `__sub__` [method] L291 - minimal +- `__rsub__` [method] L335 - minimal +- `__mul__` [method] L379 - minimal +- `__rmul__` [method] L424 - minimal +- `__truediv__` [method] L459 - minimal +- `__rtruediv__` [method] L492 - minimal +- `__pow__` [method] L528 - minimal +- `__rpow__` [method] L563 - minimal +- `__neg__` [method] L583 - minimal +- `__pos__` [method] L592 - minimal +- `__getattr__` [method] L720 - minimal +- `_sympy_` [method] L820 - minimal +- `__getitem__` [method] L964 - partial +- `_repr_latex_` [method] L978 - minimal +- `__len__` [method] L991 - minimal +- `__getattr__` [method] L1021 - minimal + +
+ +### `src/underworld3/utilities/nd_array_callback.py` + +**Public API:** + +- 📝 `DelayedCallbackManager` [class] L36 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `NDArray_With_Callback` [class] L106 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `is_delaying` (DelayedCallbackManager) [method] L54 + - NEEDS_PARAMETERS +- ⚠️ `push_delay_context` (DelayedCallbackManager) [method] L59 + - NEEDS_PARAMETERS +- ⚠️ `pop_delay_context` (DelayedCallbackManager) [method] L69 + - NEEDS_PARAMETERS +- ⚠️ `add_delayed_callback` (DelayedCallbackManager) [method] L90 + - NEEDS_PARAMETERS +- 📝 `mask` (NDArray_With_Callback) [property] L214 + - NEEDS_PARAMETERS +- ⚠️ `mask` (NDArray_With_Callback) [method] L223 + - NEEDS_PARAMETERS +- ✅ `filled` (NDArray_With_Callback) [method] L227 +- 📝 `set_callback` (NDArray_With_Callback) [method] L289 + - NEEDS_RETURNS +- 📝 `add_callback` (NDArray_With_Callback) [method] L302 + - NEEDS_RETURNS +- 📝 `remove_callback` (NDArray_With_Callback) [method] L314 + - NEEDS_RETURNS +- ⚠️ `clear_callbacks` (NDArray_With_Callback) [method] L326 + - NEEDS_PARAMETERS +- ⚠️ `enable_callbacks` (NDArray_With_Callback) [method] L330 + - NEEDS_PARAMETERS +- ⚠️ `disable_callbacks` (NDArray_With_Callback) [method] L334 + - NEEDS_PARAMETERS +- ⚠️ `owner` (NDArray_With_Callback) [property] L339 + - NEEDS_PARAMETERS +- 📝 `delay_callback` (NDArray_With_Callback) [method] L343 + - NEEDS_RETURNS +- 📝 `delay_callbacks_global` (NDArray_With_Callback) [method] L415 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `fill` (NDArray_With_Callback) [method] L755 + - NEEDS_PARAMETERS +- ⚠️ `sort` (NDArray_With_Callback) [method] L765 + - NEEDS_PARAMETERS +- ⚠️ `resize` (NDArray_With_Callback) [method] L775 + - NEEDS_PARAMETERS +- ⚠️ `copy` (NDArray_With_Callback) [method] L787 + - NEEDS_PARAMETERS +- ⚠️ `view` (NDArray_With_Callback) [method] L800 + - NEEDS_PARAMETERS +- ✅ `sync_data` (NDArray_With_Callback) [method] L827 +- ✅ `global_max` (NDArray_With_Callback) [method] L950 +- ✅ `global_min` (NDArray_With_Callback) [method] L1023 +- ✅ `global_sum` (NDArray_With_Callback) [method] L1094 +- ✅ `global_mean` (NDArray_With_Callback) [method] L1145 +- 📝 `global_size` (NDArray_With_Callback) [method] L1194 + - NEEDS_PARAMETERS +- ✅ `global_norm` (NDArray_With_Callback) [method] L1215 +- 📝 `global_rms` (NDArray_With_Callback) [method] L1253 + - NEEDS_PARAMETERS + +
+Private/Internal (25 items) + +- `__init__` [method] L44 - none +- `_get_state` [method] L47 - minimal +- `__new__` [method] L150 - partial +- `__array_finalize__` [method] L185 - minimal +- `_mask` [property] L203 - minimal +- `_mask` [method] L208 - minimal +- `_update_from` [method] L246 - minimal +- `__array_wrap__` [method] L254 - partial +- `_trigger_callback` [method] L482 - partial +- `__setitem__` [method] L527 - minimal +- `__iadd__` [method] L551 - minimal +- `__isub__` [method] L568 - minimal +- `__imul__` [method] L585 - minimal +- `__itruediv__` [method] L602 - minimal +- `__ifloordiv__` [method] L619 - minimal +- `__imod__` [method] L636 - minimal +- `__ipow__` [method] L653 - minimal +- `__iand__` [method] L670 - minimal +- `__ior__` [method] L687 - minimal +- `__ixor__` [method] L704 - minimal +- `__ilshift__` [method] L721 - minimal +- `__irshift__` [method] L738 - minimal +- `__reduce__` [method] L909 - minimal +- `__setstate__` [method] L924 - minimal +- `__repr__` [method] L935 - minimal + +
+ +### `src/underworld3/utilities/unit_aware_array.py` + +**Public API:** + +- 📝 `UnitAwareArray` [class] L33 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `units` (UnitAwareArray) [property] L155 + - NEEDS_PARAMETERS +- ⚠️ `has_units` (UnitAwareArray) [property] L160 + - NEEDS_PARAMETERS +- ⚠️ `dimensionality` (UnitAwareArray) [property] L165 + - NEEDS_PARAMETERS +- ⚠️ `unit_checking` (UnitAwareArray) [property] L178 + - NEEDS_PARAMETERS +- ⚠️ `unit_checking` (UnitAwareArray) [method] L183 + - NEEDS_PARAMETERS +- ⚠️ `auto_convert` (UnitAwareArray) [property] L188 + - NEEDS_PARAMETERS +- ⚠️ `auto_convert` (UnitAwareArray) [method] L193 + - NEEDS_PARAMETERS +- 📝 `magnitude` (UnitAwareArray) [property] L198 + - NEEDS_PARAMETERS +- ✅ `to` (UnitAwareArray) [method] L220 +- ⚠️ `max` (UnitAwareArray) [method] L497 + - NEEDS_PARAMETERS +- ⚠️ `min` (UnitAwareArray) [method] L508 + - NEEDS_PARAMETERS +- ⚠️ `mean` (UnitAwareArray) [method] L519 + - NEEDS_PARAMETERS +- ⚠️ `sum` (UnitAwareArray) [method] L530 + - NEEDS_PARAMETERS +- ⚠️ `std` (UnitAwareArray) [method] L543 + - NEEDS_PARAMETERS +- ⚠️ `var` (UnitAwareArray) [method] L574 + - NEEDS_PARAMETERS +- ✅ `global_max` (UnitAwareArray) [method] L638 +- ✅ `global_min` (UnitAwareArray) [method] L684 +- ✅ `global_sum` (UnitAwareArray) [method] L730 +- ✅ `global_mean` (UnitAwareArray) [method] L778 +- ✅ `global_var` (UnitAwareArray) [method] L826 +- ✅ `global_std` (UnitAwareArray) [method] L944 +- ✅ `global_norm` (UnitAwareArray) [method] L1001 +- 📝 `global_size` (UnitAwareArray) [method] L1039 + - NEEDS_PARAMETERS +- 📝 `global_rms` (UnitAwareArray) [method] L1060 + - NEEDS_PARAMETERS +- ⚠️ `copy` (UnitAwareArray) [method] L1268 + - NEEDS_PARAMETERS +- ⚠️ `astype` (UnitAwareArray) [method] L1278 + - NEEDS_PARAMETERS +- ⚠️ `view` (UnitAwareArray) [method] L1292 + - NEEDS_PARAMETERS +- ⚠️ `reshape` (UnitAwareArray) [method] L1311 + - NEEDS_PARAMETERS +- ⚠️ `flatten` (UnitAwareArray) [method] L1321 + - NEEDS_PARAMETERS +- ⚠️ `squeeze` (UnitAwareArray) [method] L1331 + - NEEDS_PARAMETERS +- ⚠️ `transpose` (UnitAwareArray) [method] L1341 + - NEEDS_PARAMETERS +- ✅ `create_unit_aware_array` [function] L1608 +- ✅ `zeros_with_units` [function] L1629 +- ✅ `ones_with_units` [function] L1653 +- ✅ `full_with_units` [function] L1677 +- ⚠️ `test_unit_aware_array` [function] L1704 + - NEEDS_PARAMETERS + +
+Private/Internal (17 items) + +- `__new__` [method] L63 - partial +- `__array_finalize__` [method] L108 - minimal +- `_set_units` [method] L122 - partial +- `_check_unit_compatibility` [method] L268 - complete +- `_wrap_result` [method] L447 - complete +- `_wrap_scalar_result` [method] L489 - minimal +- `__add__` [method] L1103 - minimal +- `__radd__` [method] L1113 - minimal +- `__sub__` [method] L1123 - minimal +- `__rsub__` [method] L1135 - minimal +- `__mul__` [method] L1147 - minimal +- `__rmul__` [method] L1174 - minimal +- `__truediv__` [method] L1202 - minimal +- `__rtruediv__` [method] L1216 - minimal +- `__repr__` [method] L1230 - minimal +- `__str__` [method] L1261 - minimal +- `__array_function__` [method] L1353 - complete + +
+ +### `src/underworld3/utilities/unit_aware_coordinates.py` + +**Public API:** + +- 📝 `UnitAwareBaseScalar` [class] L13 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `TimeSymbol` [class] L86 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ❌ `units` (TimeSymbol) [property] L103 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `units` (TimeSymbol) [method] L107 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `get_units` (TimeSymbol) [method] L110 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `units` (UnitAwareBaseScalar) [property] L54 + - NEEDS_PARAMETERS +- ⚠️ `units` (UnitAwareBaseScalar) [method] L59 + - NEEDS_PARAMETERS +- 📝 `get_units` (UnitAwareBaseScalar) [method] L76 + - NEEDS_PARAMETERS +- ✅ `create_unit_aware_coordinate_system` [function] L117 +- 📝 `patch_coordinate_units` [function] L178 + - NEEDS_RETURNS +- ✅ `get_coordinate_units` [function] L276 + +
+Private/Internal (5 items) + +- `__init__` [method] L47 - minimal +- `__new__` [method] L98 - none +- `_ccode` [method] L113 - none +- `__new__` [method] L28 - partial +- `_patch_time_units` [function] L245 - partial + +
+ +### `src/underworld3/__init__.py` + +**Public API:** + +- ❌ `view` [function] L166 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- 📝 `use_nondimensional_scaling` [function] L342 + - NEEDS_RETURNS +- 📝 `is_nondimensional_scaling_active` [function] L401 + - NEEDS_PARAMETERS +- ✅ `nondimensional_scaling_context` [function] L418 +- 📝 `use_strict_units` [function] L466 + - NEEDS_RETURNS +- 📝 `is_strict_units_active` [function] L521 + - NEEDS_PARAMETERS +- 📝 `unwrap` [function] L567 + - NEEDS_RETURNS +- ✅ `synchronised_array_update` [function] L616 + +
+Private/Internal (5 items) + +- `_parse_thread_env` [function] L94 - minimal +- `_apply_mpi_thread_policy` [function] L105 - partial +- `_units_view` [function] L275 - minimal +- `_apply_scaling` [function] L545 - minimal +- `_is_scaling_active` [function] L556 - minimal + +
+ +### `src/underworld3/adaptivity.py` + +**Public API:** + +- ❌ `mesh_adapt_meshVar` [function] L588 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ✅ `create_metric` [function] L88 +- ✅ `metric_from_gradient` [function] L159 +- ✅ `metric_from_field` [function] L322 +- 📝 `mesh2mesh_swarm` [function] L629 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `mesh2mesh_meshVariable` [function] L870 + - NEEDS_PARAMETERS + +
+Private/Internal (2 items) + +- `_dm_stack_bcs` [function] L451 - none +- `_dm_unstack_bcs` [function] L475 - partial + +
+ +### `src/underworld3/cython/petsc_discretisation.pyx` + +**Public API:** + +- ❌ `petsc_dm_get_periodicity` [function] L251 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `petsc_vec_concatenate` [function] L324 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `petsc_get_swarm_coord_name` [function] L347 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `petsc_fvm_get_min_radius` [function] L15 + - NEEDS_PARAMETERS +- ⚠️ `petsc_fvm_get_local_cell_sizes` [function] L36 + - NEEDS_PARAMETERS +- 📝 `petsc_dmplex_load_local_vector` [function] L63 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `petsc_dm_create_submesh_from_label` [function] L91 +- ✅ `petsc_dm_filter_by_label` [function] L131 +- 📝 `petsc_dm_set_regular_refinement` [function] L166 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `petsc_dm_find_labeled_points_local` [function] L187 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `petsc_dm_set_periodicity` [function] L286 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +### `src/underworld3/discretisation/discretisation_mesh_variables.py` + +**Public API:** + +- ❌ `extend_enum` [function] L61 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `remesh_policy` (_BaseMeshVariable) [method] L523 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ❌ `load_from_h5_plex_vector` (_BaseMeshVariable) [method] L1455 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- ⚠️ `units` (_BaseMeshVariable) [property] L482 + - NEEDS_PARAMETERS +- ⚠️ `units` (_BaseMeshVariable) [method] L487 + - NEEDS_PARAMETERS +- ⚠️ `has_units` (_BaseMeshVariable) [property] L492 + - NEEDS_PARAMETERS +- ⚠️ `dimensionality` (_BaseMeshVariable) [property] L497 + - NEEDS_PARAMETERS +- 📝 `remesh_policy` (_BaseMeshVariable) [property] L510 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `clone` (_BaseMeshVariable) [method] L696 +- 📝 `pack_raw_data_to_petsc` (_BaseMeshVariable) [method] L727 + - NEEDS_RETURNS +- 📝 `pack_uw_data_to_petsc` (_BaseMeshVariable) [method] L781 + - NEEDS_RETURNS +- ✅ `unpack_raw_data_from_petsc` (_BaseMeshVariable) [method] L884 +- ✅ `unpack_uw_data_from_petsc` (_BaseMeshVariable) [method] L933 +- ✅ `rbf_interpolate` (_BaseMeshVariable) [method] L1008 +- 📝 `save` (_BaseMeshVariable) [method] L1055 + - NEEDS_RETURNS +- 📝 `write` (_BaseMeshVariable) [method] L1136 + - NEEDS_RETURNS +- 📝 `read_timestep` (_BaseMeshVariable) [method] L1239 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `read_checkpoint` (_BaseMeshVariable) [method] L1486 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `fn` (_BaseMeshVariable) [property] L1570 + - NEEDS_PARAMETERS +- ⚠️ `ijk` (_BaseMeshVariable) [property] L1577 + - NEEDS_PARAMETERS +- 📝 `sym` (_BaseMeshVariable) [property] L1584 + - NEEDS_PARAMETERS +- 📝 `sym_1d` (_BaseMeshVariable) [property] L1624 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ⚠️ `mesh` (_BaseMeshVariable) [property] L1897 + - NEEDS_PARAMETERS +- ⚠️ `vec` (_BaseMeshVariable) [property] L1914 + - NEEDS_PARAMETERS +- 📝 `old_data` (_BaseMeshVariable) [property] L1946 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `array` (_BaseMeshVariable) [property] L1961 + - NEEDS_PARAMETERS +- 📝 `data` (_BaseMeshVariable) [property] L2690 + - NEEDS_PARAMETERS +- ⚠️ `array` (_BaseMeshVariable) [method] L2818 + - NEEDS_PARAMETERS +- 📝 `min` (_BaseMeshVariable) [method] L2872 + - NEEDS_PARAMETERS +- 📝 `max` (_BaseMeshVariable) [method] L2900 + - NEEDS_PARAMETERS +- 📝 `sum` (_BaseMeshVariable) [method] L2928 + - NEEDS_PARAMETERS +- 📝 `norm` (_BaseMeshVariable) [method] L2956 + - NEEDS_PARAMETERS +- 📝 `mean` (_BaseMeshVariable) [method] L2989 + - NEEDS_PARAMETERS +- 📝 `std` (_BaseMeshVariable) [method] L3016 + - NEEDS_PARAMETERS +- 📝 `stats` (_BaseMeshVariable) [method] L3072 + - NEEDS_PARAMETERS +- 📝 `coords` (_BaseMeshVariable) [property] L3238 + - NEEDS_PARAMETERS +- 📝 `coords_nd` (_BaseMeshVariable) [property] L3300 + - NEEDS_PARAMETERS +- 📝 `divergence` (_BaseMeshVariable) [method] L3340 + - NEEDS_PARAMETERS +- 📝 `gradient` (_BaseMeshVariable) [method] L3357 + - NEEDS_PARAMETERS +- 📝 `curl` (_BaseMeshVariable) [method] L3372 + - NEEDS_PARAMETERS +- 📝 `jacobian` (_BaseMeshVariable) [method] L3387 + - NEEDS_PARAMETERS + +
+Private/Internal (22 items) + +- `_BaseMeshVariable` [class] L73 - partial +- `__init__` [method] L158 - partial +- `_data_layout` [method] L1654 - none +- `_setup_ds` [method] L1699 - none +- `_set_vec` [method] L1796 - none +- `__del__` [method] L1890 - none +- `__new__` [method] L102 - minimal +- `_create_variable_array` [method] L530 - complete +- `_create_flat_data_array` [method] L601 - complete +- `_object_viewer` [method] L667 - minimal +- `_get_kdtree` [method] L992 - minimal +- `_replace_from_adapted_mesh` [method] L1808 - complete +- `_sync_lvec_to_gvec` [method] L1927 - partial +- `_create_array_view` [method] L2014 - partial +- `_is_simple_variable` [method] L2032 - minimal +- `_create_simple_array_view` [method] L2036 - minimal +- `_create_tensor_array_view` [method] L2372 - minimal +- `_create_canonical_data_array` [method] L2747 - partial +- `_dimensionalise_stat` [method] L2827 - complete +- `_scalar_stats` [method] L3135 - minimal +- `_vector_stats` [method] L3159 - minimal +- `_tensor_stats` [method] L3197 - minimal + +
+ +### `src/underworld3/utilities/geometry_tools.py` + +**Public API:** + +- ❌ `points_in_simplex3D` [function] L46 + - NEEDS_OVERVIEW, NEEDS_PARAMETERS +- 📝 `points_in_simplex2D` [function] L13 + - NEEDS_PARAMETERS +- 📝 `distance_pointcloud_triangle` [function] L80 + - NEEDS_PARAMETERS +- 📝 `distance_pointcloud_linesegment` [function] L160 + - NEEDS_PARAMETERS +- ✅ `signed_distance_pointcloud_linesegment_2d` [function] L211 +- ✅ `linesegment_normals_2d` [function] L254 +- ✅ `distance_pointcloud_polyline` [function] L311 +- ✅ `signed_distance_pointcloud_polyline_2d` [function] L347 + +### `src/underworld3/checkpoint/disk_snapshot.py` + +**Public API:** + +- 📝 `write_snapshot_skeleton` [function] L162 + - NEEDS_PARAMETERS +- 📝 `read_snapshot_metadata` [function] L199 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `write_snapshot` [function] L276 + - NEEDS_PARAMETERS +- 📝 `read_snapshot` [function] L449 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `is_snapshot_wrapper` [function] L892 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `extract_var_via_bridge` [function] L908 + - NEEDS_PARAMETERS +- 📝 `inspect_snapshot` [function] L967 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +
+Private/Internal (15 items) + +- `_is_h5_attr_scalar` [function] L748 - none +- `_collect_metadata` [function] L69 - partial +- `_write_metadata_attrs` [function] L156 - minimal +- `_bulk_dir_for` [function] L259 - minimal +- `_sanitise` [function] L265 - partial +- `_swarm_safe_name` [function] L569 - minimal +- `_swarm_sidecar_filename` [function] L576 - partial +- `_swarm_sidecar_pattern` [function] L586 - minimal +- `_write_swarm_to_sidecar` [function] L592 - partial +- `_read_swarm_from_sidecar` [function] L645 - partial +- `_serialise_field` [function] L754 - partial +- `_group_to_dict` [function] L801 - minimal +- `_deserialise_field` [function] L829 - partial +- `_write_state_bearer_to_group` [function] L860 - minimal +- `_read_state_bearer_into` [function] L871 - partial
@@ -2755,15 +4079,49 @@ Generated by `docstring_sweep.py` **Public API:** -- ✅ `with_units` [function] L94 -- ✅ `derivative` [function] L162 +- ✅ `with_units` [function] L98 +- ✅ `derivative` [function] L166 + +### `src/underworld3/function/field_projection.py` + +**Public API:** + +- ✅ `project_to_degree` [function] L26 +- ✅ `project_to_vertices` [function] L112 +- 📝 `write_vertices_to_viewer` [function] L229 + - NEEDS_RETURNS +- 📝 `write_coordinates_to_viewer` [function] L299 + - NEEDS_RETURNS +- 📝 `write_cell_field_to_viewer` [function] L323 + - NEEDS_RETURNS + +
+Private/Internal (2 items) + +- `_repack_tensor_to_paraview` [function] L128 - partial +- `_write_vec_to_group` [function] L194 - partial + +
### `src/underworld3/function/functions_unit_system.py` **Public API:** -- ✅ `evaluate` [function] L33 -- ✅ `global_evaluate` [function] L368 +- 📝 `evaluate` [function] L825 + - NEEDS_RETURNS +- 📝 `global_evaluate` [function] L903 + - NEEDS_RETURNS + +
+Private/Internal (5 items) + +- `_validate_coords_not_sequence` [function] L32 - partial +- `_evaluate_impl` [function] L56 - complete +- `_global_evaluate_impl` [function] L410 - complete +- `_normalize_monotone` [function] L656 - partial +- `_apply_monotone_limit` [function] L675 - partial + +
### `src/underworld3/function/gradient_evaluation.py` @@ -2788,9 +4146,9 @@ Generated by `docstring_sweep.py` **Public API:** - ✅ `is_pure_sympy_expression` [function] L22 -- ✅ `get_cached_lambdified` [function] L145 -- ✅ `evaluate_pure_sympy` [function] L199 -- ⚠️ `clear_lambdify_cache` [function] L426 +- ✅ `get_cached_lambdified` [function] L158 +- ✅ `evaluate_pure_sympy` [function] L212 +- ⚠️ `clear_lambdify_cache` [function] L441 - NEEDS_PARAMETERS
@@ -2805,27 +4163,29 @@ Generated by `docstring_sweep.py` **Public API:** - ✅ `has_units` [function] L95 -- ✅ `compute_expression_units` [function] L133 -- ✅ `get_mesh_coordinate_units` [function] L379 -- ✅ `convert_coordinates_to_mesh_units` [function] L421 -- ✅ `detect_coordinate_units` [function] L494 -- ✅ `add_expression_units_to_result` [function] L515 -- ✅ `determine_expression_units` [function] L554 -- ✅ `add_units_to_result` [function] L588 -- ✅ `convert_quantity_units` [function] L609 -- ✅ `detect_quantity_units` [function] L643 -- ✅ `make_dimensionless` [function] L706 -- ✅ `convert_array_units` [function] L795 -- ✅ `auto_convert_to_mesh_units` [function] L828 -- ✅ `convert_evaluation_result` [function] L848 -- ✅ `add_units` [function] L867 -- ✅ `make_evaluate_unit_aware` [function] L888 +- 📝 `get_units` [function] L128 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- ✅ `compute_expression_units` [function] L140 +- ✅ `get_mesh_coordinate_units` [function] L386 +- ✅ `convert_coordinates_to_mesh_units` [function] L428 +- ✅ `detect_coordinate_units` [function] L501 +- ✅ `add_expression_units_to_result` [function] L522 +- ✅ `determine_expression_units` [function] L561 +- ✅ `add_units_to_result` [function] L595 +- ✅ `convert_quantity_units` [function] L616 +- ✅ `detect_quantity_units` [function] L650 +- ✅ `make_dimensionless` [function] L713 +- ✅ `convert_array_units` [function] L802 +- ✅ `auto_convert_to_mesh_units` [function] L835 +- ✅ `convert_evaluation_result` [function] L855 +- ✅ `add_units` [function] L874 +- ✅ `make_evaluate_unit_aware` [function] L895
Private/Internal (2 items) - `_extract_value` [function] L15 - complete -- `_convert_coords_to_si` [function] L1001 - partial +- `_convert_coords_to_si` [function] L1008 - partial
@@ -2846,20 +4206,20 @@ Generated by `docstring_sweep.py` - NEEDS_PARAMETERS - ⚠️ `tensor_rotation` [function] L77 - NEEDS_PARAMETERS -- ⚠️ `rank2_to_voigt` [function] L179 +- ⚠️ `rank2_to_voigt` [function] L192 - NEEDS_PARAMETERS -- ⚠️ `voigt_to_rank2` [function] L197 +- ⚠️ `voigt_to_rank2` [function] L210 - NEEDS_PARAMETERS -- ⚠️ `rank4_to_voigt` [function] L215 +- ⚠️ `rank4_to_voigt` [function] L228 - NEEDS_PARAMETERS -- ⚠️ `voigt_to_rank4` [function] L227 +- ⚠️ `voigt_to_rank4` [function] L240 - NEEDS_PARAMETERS -- ✅ `rank2_to_mandel` [function] L236 -- ✅ `rank4_to_mandel` [function] L262 -- ✅ `mandel_to_rank2` [function] L284 -- ✅ `mandel_to_rank4` [function] L305 -- ✅ `rank4_identity` [function] L326 -- ✅ `rank2_inner_product` [function] L360 +- ✅ `rank2_to_mandel` [function] L249 +- ✅ `rank4_to_mandel` [function] L275 +- ✅ `mandel_to_rank2` [function] L297 +- ✅ `mandel_to_rank4` [function] L318 +- ✅ `rank4_identity` [function] L339 +- ✅ `rank2_inner_product` [function] L373
Private/Internal (4 items) @@ -2867,7 +4227,7 @@ Generated by `docstring_sweep.py` - `_rank2_to_unscaled_matrix` [function] L91 - minimal - `_rank4_to_unscaled_matrix` [function] L112 - minimal - `_unscaled_matrix_to_rank2` [function] L134 - minimal -- `_unscaled_matrix_to_rank4` [function] L153 - minimal +- `_unscaled_matrix_to_rank4` [function] L155 - minimal
@@ -2877,25 +4237,25 @@ Generated by `docstring_sweep.py` - ✅ `QuarterAnnulus` [function] L33 - ✅ `Annulus` [function] L273 -- ✅ `SegmentofAnnulus` [function] L545 -- ✅ `AnnulusWithSpokes` [function] L802 +- ✅ `SegmentofAnnulus` [function] L556 +- ✅ `AnnulusWithSpokes` [function] L798 - ✅ `AnnulusInternalBoundary` [function] L1132 -- ✅ `DiscInternalBoundaries` [function] L1414 +- ✅ `DiscInternalBoundaries` [function] L1428 ### `src/underworld3/meshing/cartesian.py` **Public API:** - ✅ `UnstructuredSimplexBox` [function] L33 -- ✅ `BoxInternalBoundary` [function] L360 -- ✅ `StructuredQuadBox` [function] L898 +- ✅ `BoxInternalBoundary` [function] L365 +- ✅ `StructuredQuadBox` [function] L967 ### `src/underworld3/meshing/geographic.py` **Public API:** - ✅ `RegionalSphericalBox` [function] L29 -- ✅ `RegionalGeographicBox` [function] L410 +- ✅ `RegionalGeographicBox` [function] L394 ### `src/underworld3/meshing/segmented.py` @@ -2903,16 +4263,78 @@ Generated by `docstring_sweep.py` - ✅ `SegmentedSphericalSurface2D` [function] L30 - ✅ `SegmentedSphericalShell` [function] L238 -- ✅ `SegmentedSphericalBall` [function] L683 +- ✅ `SegmentedSphericalBall` [function] L691 + +### `src/underworld3/meshing/smoothing.py` + +**Public API:** + +- ✅ `mesh_metric_mismatch` [function] L360 +- 📝 `smooth_surface_field` [function] L2499 + - NEEDS_RETURNS +- 📝 `smooth_mesh_interior` [function] L2626 + - NEEDS_RETURNS +- ✅ `metric_density_from_gradient` [function] L3776 +- ✅ `follow_metric` [function] L4183 + +
+Private/Internal (24 items) + +- `_auto_pinned_labels` [function] L136 - partial +- `_owned_vertex_mask` [function] L154 - partial +- `_pinned_mask` [function] L178 - partial +- `_build_scalar_dm` [function] L228 - partial +- `_build_adjacency_matrix` [function] L245 - partial +- `_min_incident_edge` [function] L305 - partial +- `_tri_cells` [function] L329 - partial +- `_signed_areas` [function] L351 - minimal +- `_edge_pairs` [function] L483 - partial +- `_winslow_spring` [function] L506 - partial +- `_use_direct_solver` [function] L848 - partial +- `_use_iterative_solver` [function] L933 - partial +- `_patch_volumes` [function] L1047 - partial +- `_lumped_vertex_volumes` [function] L1071 - partial +- `_hessian_recovery_class` [function] L1114 - partial +- `_winslow_elliptic` [function] L1171 - partial +- `_winslow_equidistribute` [function] L1462 - partial +- `_winslow_anisotropic` [function] L1727 - partial +- `_build_local_to_owned_map` [function] L2470 - partial +- `_tet_cells` [function] L2945 - partial +- `_owned_cell_mask` [function] L2964 - partial +- `_min_incident_edge_nd` [function] L2989 - partial +- `_winslow_mmpde` [function] L3007 - partial +- `_smooth_mesh_interior_bare` [function] L3541 - partial + +
### `src/underworld3/meshing/spherical.py` **Public API:** - ✅ `SphericalShell` [function] L30 -- ✅ `SphericalShellInternalBoundary` [function] L276 -- ✅ `SegmentofSphere` [function] L496 -- ✅ `CubedSphere` [function] L773 +- ✅ `SphericalManifold` [function] L291 +- ✅ `SphericalShellInternalBoundary` [function] L500 +- ✅ `SegmentofSphere` [function] L787 +- ✅ `CubedSphere` [function] L1048 + +### `src/underworld3/scaling/_scaling.py` + +**Public API:** + +- ⚠️ `get_coefficients` [function] L83 + - NEEDS_PARAMETERS +- ✅ `non_dimensionalise` [function] L98 +- ✅ `dimensionalise` [function] L279 +- ⚠️ `ndargs` [function] L352 + - NEEDS_PARAMETERS + +
+Private/Internal (2 items) + +- `_add_planetary_units` [function] L24 - minimal +- `_units_view` [function] L370 - partial + +
### `src/underworld3/timing.py` @@ -2926,21 +4348,279 @@ Generated by `docstring_sweep.py` - NEEDS_PARAMETERS, NEEDS_RETURNS - 📝 `print_table` [function] L138 - NEEDS_RETURNS -- 📝 `enable_petsc_logging` [function] L176 +- 📝 `enable_petsc_logging` [function] L192 - NEEDS_PARAMETERS, NEEDS_RETURNS -- 📝 `print_petsc_log` [function] L195 +- 📝 `print_petsc_log` [function] L211 - NEEDS_RETURNS -- ✅ `routine_timer_decorator` [function] L256 -- 📝 `add_timing_to_module` [function] L379 +- ✅ `routine_timer_decorator` [function] L272 +- 📝 `add_timing_to_module` [function] L395 - NEEDS_RETURNS -- ✅ `create_event` [function] L457 -- ✅ `get_summary` [function] L493 -- 📝 `print_summary` [function] L607 +- ✅ `create_event` [function] L473 +- ✅ `get_summary` [function] L509 +- 📝 `print_summary` [function] L623 - NEEDS_RETURNS
Private/Internal (1 items) -- `_class_timer_decorator` [function] L318 - complete +- `_class_timer_decorator` [function] L334 - complete + +
+ +### `src/underworld3/utilities/_jit_cache.py` + +**Public API:** + +- ⚠️ `get_cache_dir` [function] L39 + - NEEDS_PARAMETERS +- ✅ `load_module` [function] L101 +- 📝 `store_module` [function] L154 + - NEEDS_RETURNS + +
+Private/Internal (5 items) + +- `_so_path` [function] L52 - none +- `_manifest_path` [function] L56 - none +- `_lock_path` [function] L60 - none +- `_file_lock` [function] L65 - partial +- `_manifest_from_constants` [function] L91 - partial + +
+ +### `src/underworld3/utilities/_petsc_tools.py` + +**Public API:** + +- ⚠️ `require_dirs` [function] L9 + - NEEDS_PARAMETERS +- ⚠️ `parse_cmd_line_options` [function] L19 + - NEEDS_PARAMETERS + +### `src/underworld3/utilities/boundary_flux.py` + +**Public API:** + +- 📝 `boundary_flux` [function] L201 + - NEEDS_PARAMETERS +- 📝 `write_boundary_scalar_field` [function] L231 + - NEEDS_PARAMETERS +- 📝 `boundary_flux_field` [function] L256 + - NEEDS_RETURNS +- ⚠️ `boundary_flux_to_field` [function] L286 + - NEEDS_PARAMETERS + +
+Private/Internal (6 items) + +- `_key` [function] L27 - none +- `_boundary_stratum_is` [function] L31 - partial +- `_point_coord` [function] L47 - minimal +- `_boundary_field_nodes` [function] L57 - partial +- `_node_normals` [function] L85 - partial +- `_desmear` [function] L129 - partial + +
+ +### `src/underworld3/utilities/create_dmplex_from_medit.py` + +**Public API:** + +- ⚠️ `create_dmplex_from_medit` [function] L17 + - NEEDS_PARAMETERS + +### `src/underworld3/utilities/memprobe.py` + +**Public API:** + +- ⚠️ `enable` [function] L69 + - NEEDS_PARAMETERS +- ⚠️ `disable` [function] L75 + - NEEDS_PARAMETERS +- ✅ `snapshot` [function] L146 +- 📝 `diff` [function] L171 + - NEEDS_PARAMETERS +- ⚠️ `format_diff` [function] L209 + - NEEDS_PARAMETERS +- 📝 `probe` [function] L229 + - NEEDS_RETURNS +- 📝 `instrument` [function] L253 + - NEEDS_PARAMETERS +- 📝 `dump_petsc_leaks_at_finalize` [function] L276 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +
+Private/Internal (3 items) + +- `_kdtree_counts` [function] L117 - none +- `_rss_mb` [function] L81 - partial +- `_python_class_counts` [function] L129 - partial + +
+ +### `src/underworld3/utilities/nondimensional.py` + +**Public API:** + +- 📝 `derive_scaling_coefficients` [function] L12 + - NEEDS_RETURNS +- ✅ `get_required_reference_quantities` [function] L210 +- ✅ `validate_variable_reference_quantities` [function] L337 +- ✅ `apply_nondimensional_scaling` [function] L446 + +
+Private/Internal (6 items) + +- `_derive_scale_for_variable` [function] L42 - complete +- `_construct_from_fundamental_scales` [function] L85 - complete +- `_parse_dimensionality_string` [function] L151 - partial +- `_parse_dim_part` [function] L191 - minimal +- `_check_derivability_from_dimensions` [function] L269 - complete +- `_heuristic_scaling` [function] L390 - complete + +
+ +### `src/underworld3/utilities/retention_curves.py` + +**Public API:** + +- ✅ `van_genuchten_Se` [function] L68 +- ✅ `van_genuchten_theta` [function] L105 +- ✅ `van_genuchten_K` [function] L138 +- ✅ `van_genuchten_C` [function] L187 +- ✅ `gardner_K` [function] L251 +- ✅ `gardner_theta` [function] L283 +- ✅ `gardner_C` [function] L318 +- ✅ `gardner_steady_state_psi` [function] L356 +- ✅ `gardner_transient_psi` [function] L418 +- ✅ `haverkamp_theta` [function] L529 +- ✅ `haverkamp_K` [function] L579 +- ✅ `haverkamp_C` [function] L622 + +### `src/underworld3/utilities/rotated_bc.py` + +**Public API:** + +- 📝 `build_rotation` [function] L176 + - NEEDS_PARAMETERS +- 📝 `solve_rotated_freeslip` [function] L242 + - NEEDS_PARAMETERS +- 📝 `solve_rotated_freeslip_nonlinear` [function] L415 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `boundary_normal_traction` [function] L917 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `dynamic_topography_field` [function] L982 + - NEEDS_PARAMETERS + +
+Private/Internal (15 items) + +- `_velocity_field_id` [function] L61 - none +- `_warn_if_ksp_diverged` [function] L34 - partial +- `_point_coord` [function] L65 - minimal +- `_boundary_velocity_nodes` [function] L75 - partial +- `_finalize_rotated_solution` [function] L337 - partial +- `_zero_rows_local` [function] L394 - partial +- `_gather_fields_to_global` [function] L403 - minimal +- `_build_rotated_custom_Pl` [function] L616 - partial +- `_pressure_mass_schur_pmat` [function] L633 - partial +- `_velocity_diag_scale` [function] L652 - minimal +- `_destroy_rotated_ksp_ctx` [function] L665 - minimal +- `_solve_rotated_iterative` [function] L675 - partial +- `_rotated_nullspace` [function] L844 - partial +- `_mode_satisfies_constraints` [function] L894 - partial +- `_rigid_rotation_modes` [function] L1005 - partial + +
+ +### `src/underworld3/utilities/uw_swarmIO.py` + +**Public API:** + +- 📝 `swarm_h5` [function] L13 + - NEEDS_PARAMETERS, NEEDS_RETURNS +- 📝 `swarm_xdmf` [function] L60 + - NEEDS_PARAMETERS, NEEDS_RETURNS + +### `src/underworld3/visualisation/parallel.py` + +**Public API:** + +- 📝 `parallel_line_plot` [function] L13 + - NEEDS_RETURNS +- 📝 `parallel_scatter_plot` [function] L45 + - NEEDS_RETURNS +- 📝 `parallel_profile_comparison` [function] L79 + - NEEDS_RETURNS +- 📝 `parallel_custom_plot` [function] L121 + - NEEDS_RETURNS +- ⚠️ `create_line_sample` [function] L164 + - NEEDS_PARAMETERS +- ⚠️ `create_vertical_line` [function] L171 + - NEEDS_PARAMETERS +- ⚠️ `create_horizontal_line` [function] L177 + - NEEDS_PARAMETERS +- ⚠️ `create_diagonal_sample` [function] L183 + - NEEDS_PARAMETERS + +### `src/underworld3/visualisation/visualisation.py` + +**Public API:** + +- 📝 `initialise` [function] L10 + - NEEDS_RETURNS +- ✅ `mesh_to_pv_mesh` [function] L56 +- ✅ `coords_to_pv_coords` [function] L190 +- ✅ `swarm_to_pv_cloud` [function] L220 +- ✅ `meshVariable_to_pv_cloud` [function] L249 +- ✅ `meshVariable_to_pv_mesh_object` [function] L292 +- ✅ `scalar_fn_to_pv_points` [function] L346 +- ✅ `vector_fn_to_pv_points` [function] L399 +- ✅ `clip_mesh` [function] L469 +- ✅ `plot_mesh` [function] L503 +- ✅ `plot_scalar` [function] L584 +- ✅ `plot_vector` [function] L716 +- ✅ `save_colorbar` [function] L899 + +
+Private/Internal (1 items) + +- `_vector_to_pv_vector` [function] L206 - minimal + +
+ +### `src/underworld3/utilities/__init__.py` + +
+Private/Internal (1 items) + +- `_append_petsc_path` [function] L44 - none + +
+ +### `src/underworld3/meshing/_ot_adapt.py` + +
+Private/Internal (9 items) + +- `_is_radial_coords` [function] L48 - partial +- `_auto_grad_smoothing_length` [function] L62 - partial +- `_slip_normals` [function] L84 - partial +- `_ot_adapt_step` [function] L114 - partial +- `_boundary_facets` [function] L251 - partial +- `_all_boundary_labels` [function] L309 - partial +- `_resolve_slip` [function] L326 - partial +- `_nearest_on_facets_2d` [function] L372 - minimal +- `_nearest_on_facets_3d` [function] L388 - partial + +
+ +### `src/underworld3/utilities/_nb_tools.py` + +
+Private/Internal (2 items) + +- `_is_notebook` [function] L1 - partial +- `_is_interactive_vis` [function] L25 - minimal
diff --git a/docs/examples/submesh_investigation/test_region_ds_nitsche.py b/docs/examples/submesh_investigation/test_region_ds_nitsche.py index 8fd84b023..7719da69a 100644 --- a/docs/examples/submesh_investigation/test_region_ds_nitsche.py +++ b/docs/examples/submesh_investigation/test_region_ds_nitsche.py @@ -86,7 +86,7 @@ # Nitsche free-slip on internal boundary # Uses constitutive model viscosity, so it sees the contrast -stokes.add_nitsche_bc("Internal", direction=unit_rvec, gamma=10.0, theta=1) +stokes.add_nitsche_bc(0.0, "Internal", direction=unit_rvec, gamma=10.0, theta=1) # --- Solver options --- diff --git a/scripts/docstring_sweep.py b/scripts/docstring_sweep.py index 7d7a0c928..cf331250c 100644 --- a/scripts/docstring_sweep.py +++ b/scripts/docstring_sweep.py @@ -303,15 +303,19 @@ def parse_cython_file(filepath: Path) -> List[CodeElement]: elements = [] source = filepath.read_text() - # Pattern for function/method definitions + # Pattern for function/method definitions. + # NB: the leading indent group must be [ \t]* (NOT \s*): with re.MULTILINE, + # \s* consumes preceding blank lines, shifting the computed line number so + # the docstring search starts on the def/class line itself and misses the + # docstring entirely (and miscounts the indent). func_pattern = re.compile( - r'^(\s*)((?:cdef|cpdef|def)\s+\w+\s*\([^)]*\)[^:]*:)\s*$', + r'^([ \t]*)((?:cdef|cpdef|def)\s+\w+\s*\([^)]*\)[^:]*:)\s*$', re.MULTILINE ) # Pattern for class definitions class_pattern = re.compile( - r'^(\s*)((?:cdef\s+)?class\s+(\w+)[^:]*:)\s*$', + r'^([ \t]*)((?:cdef\s+)?class\s+(\w+)[^:]*:)\s*$', re.MULTILINE ) @@ -324,10 +328,15 @@ def parse_cython_file(filepath: Path) -> List[CodeElement]: class_name = match.group(3) line_no = source[:match.start()].count('\n') + 1 - # Try to extract docstring (next non-empty line with quotes) + # Try to extract docstring (next non-empty line with quotes). + # Search from the END of the (possibly multi-line) signature, not from + # the definition line, or long signatures hide their docstring. + sig_end_line = source[:match.end(2)].count('\n') + 1 docstring = None - for i in range(line_no, min(line_no + 5, len(lines))): - line = lines[i].strip() + for i in range(sig_end_line, min(sig_end_line + 5, len(lines))): + # Accept string prefixes on docstrings (r""", rb"""; common in .pyx) + line = re.sub(r"^[rRbBuUfF]{1,2}(?=\"\"\"|''')", "", + lines[i].strip()) if line.startswith('"""') or line.startswith("'''"): # Find end of docstring doc_lines = [] @@ -375,12 +384,17 @@ def parse_cython_file(filepath: Path) -> List[CodeElement]: # Skip if it's a method inside a class (indented) indent = len(match.group(1)) - # Try to extract docstring + # Try to extract docstring. + # Search from the END of the (possibly multi-line) signature, not from + # the definition line, or long signatures hide their docstring. + sig_end_line = source[:match.end(2)].count('\n') + 1 docstring = None - for i in range(line_no, min(line_no + 5, len(lines))): + for i in range(sig_end_line, min(sig_end_line + 5, len(lines))): if i >= len(lines): break - line = lines[i].strip() + # Accept string prefixes on docstrings (r""", rb"""; common in .pyx) + line = re.sub(r"^[rRbBuUfF]{1,2}(?=\"\"\"|''')", "", + lines[i].strip()) if line.startswith('"""') or line.startswith("'''"): quote = line[:3] if line.endswith(quote) and len(line) > 6: diff --git a/src/underworld3/cython/petsc_generic_snes_solvers.pyx b/src/underworld3/cython/petsc_generic_snes_solvers.pyx index 04d0b2692..3152b4244 100644 --- a/src/underworld3/cython/petsc_generic_snes_solvers.pyx +++ b/src/underworld3/cython/petsc_generic_snes_solvers.pyx @@ -5400,6 +5400,14 @@ class SNES_Stokes_SaddlePt(SolverBaseClass): raise TypeError( f"add_rotated_freeslip_bc() requires a boundary label string; " f"got {type(boundary).__name__}") + # TODO(BUG): float zero is rejected as "non-zero" by this guard. + # sympy's == is structural, so sympy.sympify(0.0) != 0 evaluates True + # (Float(0.0) is not Integer(0)): the canonical value-first call + # add_rotated_freeslip_bc(0.0, boundary) — the very form this method's + # own deprecation message recommends — raises NotImplementedError, + # while conds=0 (int) and conds=None work. The guard should test + # value semantics, e.g. `sympy.sympify(conds).is_zero is not True`. + # Found by the WE-09 call-site sweep, 2026-07-07 (Wave C shim, #334); issue #336. if conds is not None and sympy.sympify(conds) != 0: raise NotImplementedError( "add_rotated_freeslip_bc imposes u.n = 0 only; a non-zero " diff --git a/src/underworld3/units.py b/src/underworld3/units.py index 28352ddb8..3714442d0 100644 --- a/src/underworld3/units.py +++ b/src/underworld3/units.py @@ -514,23 +514,31 @@ def check_units_consistency(*expressions) -> bool: This function validates that all provided expressions have the same dimensionality, which is required for addition, subtraction, and comparison operations. - Args: - *expressions: Any number of expressions, quantities, or unit-aware objects + Parameters + ---------- + *expressions : + Any number of expressions, quantities, or unit-aware objects - Returns: - bool: True if all expressions have consistent units, False otherwise + Returns + ------- + bool + True if all expressions have consistent units, False otherwise - Raises: - DimensionalityError: If expressions have inconsistent units - NoUnitsError: If some expressions have units and others don't + Raises + ------ + DimensionalityError + If expressions have inconsistent units + NoUnitsError + If some expressions have units and others don't - Examples: - >>> velocity1 = EnhancedMeshVariable("v1", mesh, 2, units="m/s") - >>> velocity2 = EnhancedMeshVariable("v2", mesh, 2, units="km/h") - >>> pressure = EnhancedMeshVariable("p", mesh, 1, units="Pa") + Examples + -------- + >>> velocity1 = EnhancedMeshVariable("v1", mesh, 2, units="m/s") + >>> velocity2 = EnhancedMeshVariable("v2", mesh, 2, units="km/h") + >>> pressure = EnhancedMeshVariable("p", mesh, 1, units="Pa") - >>> check_units_consistency(velocity1, velocity2) # True - both velocities - >>> check_units_consistency(velocity1, pressure) # False - different dimensions + >>> check_units_consistency(velocity1, velocity2) # True - both velocities + >>> check_units_consistency(velocity1, pressure) # False - different dimensions """ if len(expressions) < 2: return True @@ -568,16 +576,20 @@ def get_dimensionality(expression) -> Optional[Any]: """ Get the dimensionality of an expression or quantity. - Args: - expression: Expression, quantity, or unit-aware object + Parameters + ---------- + expression : + Expression, quantity, or unit-aware object - Returns: - Dimensionality representation (backend-specific) or None if no units + 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] + Examples + -------- + >>> velocity = EnhancedMeshVariable("velocity", mesh, 2, units="m/s") + >>> dims = get_dimensionality(velocity) + >>> print(dims) # [length] / [time] """ has_units, units, backend = _extract_units_info(expression) @@ -600,22 +612,28 @@ def get_units(expression, simplify: bool = False) -> Optional[Any]: 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). + Parameters + ---------- + expression : + Expression, quantity, or unit-aware object + simplify : bool, default=False + If True, simplify mixed units to base SI (default False). - Returns: + Returns + ------- + pint.Unit or None 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) # - - >>> # 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!) + Examples + -------- + >>> velocity = uw.discretisation.MeshVariable("velocity", mesh, 2, units="m/s") + >>> units = uw.get_units(velocity) + >>> print(units) # + + >>> # 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!) """ has_units, units, backend = _extract_units_info(expression) @@ -655,30 +673,38 @@ def non_dimensionalise(expression, model=None) -> Any: - 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) + Parameters + ---------- + expression : + Expression, quantity, or unit-aware object to non-dimensionalise + model : Model, optional + Model instance with reference quantities (uses default if None) - Returns: - Non-dimensional value(s) with preserved dimensionality metadata + 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 + 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) + 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 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 + >>> # With plain number (no model reference quantities) + >>> plain_value = 2.5 + >>> result = non_dimensionalise(plain_value) # Returns 2.5 """ # Get model if not provided if model is None: @@ -893,22 +919,27 @@ def show_nondimensional_form(expression, model=None): Useful for seeing what will actually be compiled into C code when scaling is active. - Args: - expression: Any SymPy expression, UW expression, or variable - model: Model instance with reference quantities (uses default if None) - - Returns: - SymPy expression with non-dimensional scaling applied + Parameters + ---------- + expression : + Any SymPy expression, UW expression, or variable + model : Model, optional + Model instance with reference quantities (uses default if None) - Examples: - >>> # See what the Stokes flux looks like non-dimensionally - >>> flux = stokes.constitutive_model.flux - >>> nondim_flux = uw.show_nondimensional_form(flux) - >>> print(nondim_flux) # Should show values close to 1.0, not 1e21 + Returns + ------- + SymPy expression with non-dimensional scaling applied - >>> # Check a parameter - >>> viscosity = model.Parameters.shear_viscosity_0 - >>> print(uw.show_nondimensional_form(viscosity)) # Should be ~1.0 + Examples + -------- + >>> # See what the Stokes flux looks like non-dimensionally + >>> flux = stokes.constitutive_model.flux + >>> nondim_flux = uw.show_nondimensional_form(flux) + >>> print(nondim_flux) # Should show values close to 1.0, not 1e21 + + >>> # Check a parameter + >>> viscosity = model.Parameters.shear_viscosity_0 + >>> print(uw.show_nondimensional_form(viscosity)) # Should be ~1.0 """ import underworld3 as uw from .function.expressions import _unwrap_for_compilation @@ -1096,16 +1127,20 @@ def simplify_units(expression) -> Any: This function performs dimensional analysis to simplify unit expressions, canceling common factors and reducing to fundamental units. - Args: - expression: Expression with units to simplify + Parameters + ---------- + expression : + Expression with units to simplify - Returns: - Expression with simplified units + Returns + ------- + Expression with simplified units - Examples: - >>> # Force per area = pressure - >>> force_per_area = force / area # N/m² - >>> simplified = simplify_units(force_per_area) # Pa + Examples + -------- + >>> # Force per area = pressure + >>> force_per_area = force / area # N/m² + >>> simplified = simplify_units(force_per_area) # Pa """ has_units, units, backend = _extract_units_info(expression) @@ -1129,17 +1164,23 @@ def create_quantity(value, units: Union[str, Any], backend: Optional[str] = None function returns a raw Pint ``Quantity`` and will be removed after one deprecation cycle. - Args: - value: Numeric value or array - units: Units specification (string or units object) - backend: Backend to use ('pint' or 'sympy'), defaults to 'pint' + Parameters + ---------- + value : + Numeric value or array + units : str or units object + Units specification (string or units object) + backend : str, optional + Backend to use ('pint' or 'sympy'), defaults to 'pint' - Returns: - Dimensional quantity (raw Pint ``Quantity``) + Returns + ------- + Dimensional quantity (raw Pint ``Quantity``) - Examples: - >>> velocity_qty = create_quantity([1.0, 2.0], "m/s") - >>> pressure_qty = create_quantity(101325, "Pa") + Examples + -------- + >>> velocity_qty = create_quantity([1.0, 2.0], "m/s") + >>> pressure_qty = create_quantity(101325, "Pa") """ warnings.warn( "create_quantity is deprecated; use uw.quantity(value, units) — note " @@ -1165,25 +1206,33 @@ def convert_units(quantity, target_units: Union[str, Any]) -> Any: - 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) + Parameters + ---------- + quantity : + Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint) + target_units : str or pint.Unit + Target units for conversion (str or Pint Unit) - Returns: - Same type as input, converted to target units + Returns + ------- + Same type as input, converted to target units - Raises: - DimensionalityError: If units are not compatible for conversion - NoUnitsError: If quantity has no 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] + 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 + >>> radius = uw.expression("r", uw.quantity(6370, "km")) + >>> radius_m = uw.convert_units(radius, "m") + >>> print(radius_m.value) # 6370000.0 """ from underworld3.scaling import units as ureg @@ -1293,23 +1342,29 @@ def to_base_units(quantity) -> Any: 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) + Parameters + ---------- + quantity : + Quantity to convert (UWQuantity, UWexpression, UnitAwareArray, Pint) - Returns: - Same type as input, converted to SI base units + Returns + ------- + Same type as input, converted to SI base units - Raises: - NoUnitsError: If quantity has no 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] + 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 + >>> radius = uw.expression("r", uw.quantity(6370, "km")) + >>> radius_si = uw.to_base_units(radius) + >>> print(radius_si.value) # 6370000.0 """ from underworld3.scaling import units as ureg @@ -1387,19 +1442,25 @@ def to_reduced_units(quantity) -> Any: This is useful for complex unit expressions like (kg * m / s²) / (kg / m³) which would simplify to m⁴ / s². - Args: - quantity: Quantity to simplify (UWQuantity, UWexpression, UnitAwareArray, Pint) + Parameters + ---------- + quantity : + Quantity to simplify (UWQuantity, UWexpression, UnitAwareArray, Pint) - Returns: - Same type as input, with simplified units + Returns + ------- + Same type as input, with simplified units - Raises: - NoUnitsError: If quantity has no units + Raises + ------ + NoUnitsError + If quantity has no units - Examples: - >>> force = uw.quantity(100, "kg*m/s**2") - >>> force_simplified = uw.to_reduced_units(force) - >>> print(force_simplified) # 100.0 [newton] + Examples + -------- + >>> force = uw.quantity(100, "kg*m/s**2") + >>> force_simplified = uw.to_reduced_units(force) + >>> print(force_simplified) # 100.0 [newton] """ from underworld3.scaling import units as ureg @@ -1473,23 +1534,29 @@ def to_compact(quantity) -> Any: 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) + Parameters + ---------- + quantity : + Quantity to make compact (UWQuantity, UWexpression, UnitAwareArray, Pint) - Returns: - Same type as input, with compact units + Returns + ------- + Same type as input, with compact units - Raises: - NoUnitsError: If quantity has no 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] + 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] + >>> big_length = uw.quantity(1e9, "m") + >>> big_compact = uw.to_compact(big_length) + >>> print(big_compact) # 1.0 [gigameter] """ from underworld3.scaling import units as ureg @@ -1560,13 +1627,16 @@ def get_scaling_coefficients() -> Dict[str, Any]: """ Get the current scaling coefficients used for non-dimensionalisation. - Returns: + Returns + ------- + dict Dictionary of scaling coefficients for fundamental dimensions - Examples: - >>> coeffs = get_scaling_coefficients() - >>> print(coeffs['[length]']) # 1.0 meter - >>> print(coeffs['[time]']) # 1.0 year + Examples + -------- + >>> coeffs = get_scaling_coefficients() + >>> print(coeffs['[length]']) # 1.0 meter + >>> print(coeffs['[time]']) # 1.0 year """ # Use the existing scaling module import underworld3.scaling as scaling @@ -1578,14 +1648,17 @@ def set_scaling_coefficients(coefficients: Dict[str, Any]) -> None: """ Set custom scaling coefficients for non-dimensionalisation. - Args: - coefficients: Dictionary mapping dimension names to scaling quantities + Parameters + ---------- + coefficients : dict + Dictionary mapping dimension names to scaling quantities - Examples: - >>> coeffs = get_scaling_coefficients() - >>> coeffs['[length]'] = create_quantity(1000, "km") # Geological scale - >>> coeffs['[time]'] = create_quantity(1e6, "year") # Geological time - >>> set_scaling_coefficients(coeffs) + Examples + -------- + >>> coeffs = get_scaling_coefficients() + >>> coeffs['[length]'] = create_quantity(1000, "km") # Geological scale + >>> coeffs['[time]'] = create_quantity(1e6, "year") # Geological time + >>> set_scaling_coefficients(coeffs) """ # This would need to update the scaling module's global coefficients warnings.warn("Setting custom scaling coefficients not implemented") @@ -1634,15 +1707,22 @@ def validate_expression_units(expression, expected_units: Union[str, Any]) -> bo """ Validate that an expression has the expected units. - Args: - expression: Expression to validate - expected_units: Expected units (string or units object) + Parameters + ---------- + expression : + Expression to validate + expected_units : str or units object + Expected units (string or units object) - Returns: + Returns + ------- + bool True if units match, False otherwise - Raises: - NoUnitsError: If expression has no units but units are expected + Raises + ------ + NoUnitsError + If expression has no units but units are expected """ has_units_flag, actual_units, backend = _extract_units_info(expression) @@ -1669,42 +1749,53 @@ def assert_dimensionality( at various points in the code. Complements get_dimensionality() by providing enforcement rather than just inspection. - Args: - value: The value to validate (quantity, expression, variable, array, etc.) - expected_dimensionality: Expected dimensionality as a string - - Specific dimensionality: "[length]", "[length]/[time]", "[mass]*[length]/[time]**2" - - Dimensionless: "dimensionless" or "" - value_name: Name of the value being validated (for error messages) - allow_dimensionless: If True, accept dimensionless values even when dimensional - expected (default: True, as dimensionless is valid for solver operations) - strict: If True, raise error on dimensionless when dimensional expected - (default: False, overrides allow_dimensionless) - - Raises: - DimensionalityError: If dimensionality doesn't match expected - NoUnitsError: If strict=True and value is dimensionless when dimensional expected - - Examples: - >>> # Validate coordinates have length dimensionality - >>> coords = mesh.X.coords - >>> assert_dimensionality(coords, "[length]", "coordinates") - - >>> # Validate velocity has correct dimensionality - >>> velocity = uw.discretisation.MeshVariable("v", mesh, 2, units="m/s") - >>> assert_dimensionality(velocity, "[length]/[time]", "velocity") - - >>> # Validate pressure - >>> pressure = uw.quantity(1e5, "Pa") - >>> assert_dimensionality(pressure, "[mass]/([length]*[time]**2)", "pressure") - - >>> # Accept dimensionless when dimensional expected (default) - >>> dimensionless_coords = np.array([[0, 1], [1, 1]]) - >>> assert_dimensionality(dimensionless_coords, "[length]", "coords") # OK - - >>> # Strict mode: reject dimensionless when dimensional expected - >>> assert_dimensionality( - ... dimensionless_coords, "[length]", "coords", strict=True - ... ) # Raises NoUnitsError + Parameters + ---------- + value : + The value to validate (quantity, expression, variable, array, etc.) + expected_dimensionality : str + Expected dimensionality as a string + + - Specific dimensionality: "[length]", "[length]/[time]", "[mass]*[length]/[time]**2" + - Dimensionless: "dimensionless" or "" + value_name : str, default="value" + Name of the value being validated (for error messages) + allow_dimensionless : bool, default=True + If True, accept dimensionless values even when dimensional + expected (default: True, as dimensionless is valid for solver operations) + strict : bool, default=False + If True, raise error on dimensionless when dimensional expected + (default: False, overrides allow_dimensionless) + + Raises + ------ + DimensionalityError + If dimensionality doesn't match expected + NoUnitsError + If strict=True and value is dimensionless when dimensional expected + + Examples + -------- + >>> # Validate coordinates have length dimensionality + >>> coords = mesh.X.coords + >>> assert_dimensionality(coords, "[length]", "coordinates") + + >>> # Validate velocity has correct dimensionality + >>> velocity = uw.discretisation.MeshVariable("v", mesh, 2, units="m/s") + >>> assert_dimensionality(velocity, "[length]/[time]", "velocity") + + >>> # Validate pressure + >>> pressure = uw.quantity(1e5, "Pa") + >>> assert_dimensionality(pressure, "[mass]/([length]*[time]**2)", "pressure") + + >>> # Accept dimensionless when dimensional expected (default) + >>> dimensionless_coords = np.array([[0, 1], [1, 1]]) + >>> assert_dimensionality(dimensionless_coords, "[length]", "coords") # OK + + >>> # Strict mode: reject dimensionless when dimensional expected + >>> assert_dimensionality( + ... dimensionless_coords, "[length]", "coords", strict=True + ... ) # Raises NoUnitsError """ # Check if value has units has_units_flag, actual_units, backend = _extract_units_info(value) @@ -1795,23 +1886,28 @@ def validate_coordinates_dimensionality(coords) -> None: raise an error if coordinates have the wrong dimensionality (e.g., time, temperature, velocity). - Args: - coords: Coordinate array to validate + Parameters + ---------- + coords : + Coordinate array to validate - Raises: - DimensionalityError: If coordinates have units but not length dimensionality + Raises + ------ + DimensionalityError + If coordinates have units but not length dimensionality - Examples: - >>> # Valid: dimensionless coords (for solvers) - >>> validate_coordinates_dimensionality(np.array([[0, 1], [1, 1]])) + Examples + -------- + >>> # Valid: dimensionless coords (for solvers) + >>> validate_coordinates_dimensionality(np.array([[0, 1], [1, 1]])) - >>> # Valid: coords with length units - >>> coords = uw.function.UnitAwareArray([[0, 1000], [1000, 1000]], units="meter") - >>> validate_coordinates_dimensionality(coords) + >>> # Valid: coords with length units + >>> coords = uw.function.UnitAwareArray([[0, 1000], [1000, 1000]], units="meter") + >>> validate_coordinates_dimensionality(coords) - >>> # Invalid: coords with time units (would raise error) - >>> time_coords = uw.quantity(5.0, "second") - >>> validate_coordinates_dimensionality(time_coords) # Raises DimensionalityError + >>> # Invalid: coords with time units (would raise error) + >>> time_coords = uw.quantity(5.0, "second") + >>> validate_coordinates_dimensionality(time_coords) # Raises DimensionalityError """ # Check if coords have units has_units_flag, actual_units, backend = _extract_units_info(coords) @@ -1845,12 +1941,17 @@ def enforce_units_consistency(*expressions) -> None: """ Enforce units consistency, raising an error if inconsistent. - Args: - *expressions: Expressions that must have consistent units + Parameters + ---------- + *expressions : + Expressions that must have consistent units - Raises: - DimensionalityError: If units are inconsistent - NoUnitsError: If some have units and others don't + Raises + ------ + DimensionalityError + If units are inconsistent + NoUnitsError + If some have units and others don't """ check_units_consistency(*expressions) # This already raises appropriate errors @@ -1877,39 +1978,50 @@ def require_units_if_active( This prevents ambiguity in dimensional problems where a raw number like `400` could mean meters, kilometers, or a nondimensional value. - Args: - value: Input value (quantity, expression, or raw number) - name: Parameter name for error messages (e.g., "depth_max") - expected_dimensionality: Expected dimensionality string (e.g., "[length]") - If provided, validates the input has correct dimensionality. - default_unit: Default unit to assume for raw values when units not active. - Only used for documentation in error messages. + Parameters + ---------- + value : + Input value (quantity, expression, or raw number) + name : str + Parameter name for error messages (e.g., "depth_max") + expected_dimensionality : str, optional + Expected dimensionality string (e.g., "[length]") + If provided, validates the input has correct dimensionality. + default_unit : str, optional + Default unit to assume for raw values when units not active. + Only used for documentation in error messages. - Returns: - float: Nondimensional value (divided by appropriate reference scale) - - Raises: - ValueError: If units are active but value has no units - DimensionalityError: If value has wrong dimensionality - - Examples: - >>> # With units active - requires quantity - >>> model = uw.Model() - >>> model.set_reference_quantities(length=uw.quantity(1000, "km"), ...) - >>> depth = require_units_if_active( - ... uw.quantity(400, "km"), - ... "depth_max", - ... expected_dimensionality="[length]" - ... ) - >>> depth # Returns 0.4 (400 km / 1000 km reference) - - >>> # Without units - raw numbers accepted - >>> depth = require_units_if_active(400, "depth_max") - >>> depth # Returns 400 (unchanged) - - >>> # Error case - units active but raw number provided - >>> model.set_reference_quantities(...) - >>> require_units_if_active(400, "depth_max") # Raises ValueError + Returns + ------- + float + Nondimensional value (divided by appropriate reference scale) + + Raises + ------ + ValueError + If units are active but value has no units + DimensionalityError + If value has wrong dimensionality + + Examples + -------- + >>> # With units active - requires quantity + >>> model = uw.Model() + >>> model.set_reference_quantities(length=uw.quantity(1000, "km"), ...) + >>> depth = require_units_if_active( + ... uw.quantity(400, "km"), + ... "depth_max", + ... expected_dimensionality="[length]" + ... ) + >>> depth # Returns 0.4 (400 km / 1000 km reference) + + >>> # Without units - raw numbers accepted + >>> depth = require_units_if_active(400, "depth_max") + >>> depth # Returns 400 (unchanged) + + >>> # Error case - units active but raw number provided + >>> model.set_reference_quantities(...) + >>> require_units_if_active(400, "depth_max") # Raises ValueError """ import underworld3 as uw @@ -1970,17 +2082,23 @@ def convert_angle_to_degrees(value, name: str = "angle") -> float: - Quantities with degree units: extracted as degrees - Quantities with radian units: converted to degrees - Args: - value: Angle value (quantity or raw number) - name: Parameter name for error messages + Parameters + ---------- + value : + Angle value (quantity or raw number) + name : str, default="angle" + Parameter name for error messages - Returns: - float: Angle in degrees + Returns + ------- + float + Angle in degrees - Examples: - >>> convert_angle_to_degrees(45) # Raw number → 45 degrees - >>> convert_angle_to_degrees(uw.quantity(45, "degree")) # → 45 - >>> convert_angle_to_degrees(uw.quantity(np.pi/4, "radian")) # → 45 + Examples + -------- + >>> convert_angle_to_degrees(45) # Raw number → 45 degrees + >>> convert_angle_to_degrees(uw.quantity(45, "degree")) # → 45 + >>> convert_angle_to_degrees(uw.quantity(np.pi/4, "radian")) # → 45 """ if not has_units(value): # Raw number - assume degrees (conventional for geographic) diff --git a/tests/parallel/test_1017_custom_mg_parallel_mpi.py b/tests/parallel/test_1017_custom_mg_parallel_mpi.py index d3f925409..b1caf0a30 100644 --- a/tests/parallel/test_1017_custom_mg_parallel_mpi.py +++ b/tests/parallel/test_1017_custom_mg_parallel_mpi.py @@ -141,10 +141,10 @@ def test_parallel_custom_fmg_stokes_constrained(): s.constitutive_model = uw.constitutive_models.ViscousFlowModel s.constitutive_model.Parameters.shear_viscosity_0 = sol.fn_viscosity s.bodyforce = sol.fn_bodyforce - s.add_constraint_bc("Left", g=0.0, normal=sympy.Matrix([[-1.0, 0.0]])) - s.add_constraint_bc("Right", g=0.0, normal=sympy.Matrix([[1.0, 0.0]])) - s.add_constraint_bc("Bottom", g=0.0, normal=sympy.Matrix([[0.0, -1.0]])) - s.add_constraint_bc("Top", g=0.0, normal=sympy.Matrix([[0.0, 1.0]])) + s.add_constraint_bc(0.0, "Left", normal=sympy.Matrix([[-1.0, 0.0]])) + s.add_constraint_bc(0.0, "Right", normal=sympy.Matrix([[1.0, 0.0]])) + s.add_constraint_bc(0.0, "Bottom", normal=sympy.Matrix([[0.0, -1.0]])) + s.add_constraint_bc(0.0, "Top", normal=sympy.Matrix([[0.0, 1.0]])) s.petsc_use_pressure_nullspace = True s.tolerance = 1.0e-9 s.petsc_options["snes_type"] = "ksponly" diff --git a/tests/parallel/test_1062_constrained_stratum_guard_parallel.py b/tests/parallel/test_1062_constrained_stratum_guard_parallel.py index 16f9ce7ee..9587026db 100644 --- a/tests/parallel/test_1062_constrained_stratum_guard_parallel.py +++ b/tests/parallel/test_1062_constrained_stratum_guard_parallel.py @@ -49,10 +49,10 @@ def _solve(): # aligned split at np=2 this puts one axis-aligned pair (Left/Right or # Bottom/Top) with all points on one rank and zero on the other — the # exact configuration that triggered #291's SIGSEGV. - s.add_constraint_bc("Left", g=0.0, normal=sympy.Matrix([[-1.0, 0.0]])) - s.add_constraint_bc("Right", g=0.0, normal=sympy.Matrix([[ 1.0, 0.0]])) - s.add_constraint_bc("Bottom", g=0.0, normal=sympy.Matrix([[ 0.0,-1.0]])) - s.add_constraint_bc("Top", g=0.0, normal=sympy.Matrix([[ 0.0, 1.0]])) + s.add_constraint_bc(0.0, "Left", normal=sympy.Matrix([[-1.0, 0.0]])) + s.add_constraint_bc(0.0, "Right", normal=sympy.Matrix([[ 1.0, 0.0]])) + s.add_constraint_bc(0.0, "Bottom", normal=sympy.Matrix([[ 0.0,-1.0]])) + s.add_constraint_bc(0.0, "Top", normal=sympy.Matrix([[ 0.0, 1.0]])) s.petsc_use_pressure_nullspace = True s.tolerance = 1.0e-8 s.solve() diff --git a/tests/parallel/test_1063_constrained_freeslip_parallel.py b/tests/parallel/test_1063_constrained_freeslip_parallel.py index 0d1c75b7b..745f45b5b 100644 --- a/tests/parallel/test_1063_constrained_freeslip_parallel.py +++ b/tests/parallel/test_1063_constrained_freeslip_parallel.py @@ -63,7 +63,7 @@ def _solve_diagnostics(kind): st.constitutive_model.Parameters.shear_viscosity_0 = 1.0 st.tolerance = 1.0e-8 st.add_essential_bc((0.0, 0.0), "Lower") - h = st.add_constraint_bc("Upper") + h = st.add_constraint_bc(0.0, "Upper") st.bodyforce = 1.0e2 * sympy.sin(3 * sympy.atan2(X[1], X[0])) * unit_r st.solve(zero_init_guess=True) @@ -104,7 +104,7 @@ def _solve_gauge_diagnostics(): st.tolerance = 1.0e-10 st.petsc_use_nullspace = True # enclosed -> pressure null space active st.add_essential_bc((0.0, 0.0), "Lower") # kill the velocity rotation null space - st.add_constraint_bc("Upper", g=0.0, normal=unit_r) + st.add_constraint_bc(0.0, "Upper", normal=unit_r) st.bodyforce = 1.0e2 * sympy.sin(3 * sympy.atan2(X[1], X[0])) * unit_r st.solve(zero_init_guess=True) assert st._auto_gauge_callback is not None, "auto pressure gauge should have fired" diff --git a/tests/parallel/test_1064_rotated_freeslip_parallel.py b/tests/parallel/test_1064_rotated_freeslip_parallel.py index 0300dd01d..eeb8eb75c 100644 --- a/tests/parallel/test_1064_rotated_freeslip_parallel.py +++ b/tests/parallel/test_1064_rotated_freeslip_parallel.py @@ -86,7 +86,7 @@ def _box_diagnostics(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() @@ -112,8 +112,8 @@ def _annulus_diagnostics(): s.bodyforce = sympy.Matrix([[x / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0, y / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0]]) nhat = sympy.Matrix([[x / r, y / r]]) - s.add_rotated_freeslip_bc("Lower", normal=nhat) - s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.add_rotated_freeslip_bc(0, "Lower", normal=nhat) + s.add_rotated_freeslip_bc(0, "Upper", normal=nhat) s.tolerance = 1e-9 s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" @@ -145,8 +145,8 @@ def _spherical3d_diagnostics(): ylm = (3 * (z / r) ** 2 - 1) / 2 s.bodyforce = ylm * (r - RI) * (RO - r) * 20.0 / r * sympy.Matrix([[x, y, z]]) nhat = sympy.Matrix([[x / r, y / r, z / r]]) - s.add_rotated_freeslip_bc("Lower", normal=nhat) - s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.add_rotated_freeslip_bc(0, "Lower", normal=nhat) + s.add_rotated_freeslip_bc(0, "Upper", normal=nhat) s.tolerance = 1e-7 s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" @@ -179,8 +179,8 @@ def _annulus_fmg_diagnostics(): s.bodyforce = sympy.Matrix([[x / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0, y / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0]]) nhat = sympy.Matrix([[x / r, y / r]]) - s.add_rotated_freeslip_bc("Lower", normal=nhat) - s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.add_rotated_freeslip_bc(0, "Lower", normal=nhat) + s.add_rotated_freeslip_bc(0, "Upper", normal=nhat) s.tolerance = 1e-9 s.saddle_preconditioner = 1.0 s.petsc_use_pressure_nullspace = True @@ -222,7 +222,7 @@ def _box_nonlinear_diagnostics(): s.petsc_use_pressure_nullspace = True s.consistent_jacobian = True # Newton tangent (few iterations) for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.solve() L2 = float(np.sqrt(uw.maths.Integral(mesh, v.sym.dot(v.sym)).evaluate())) @@ -247,7 +247,7 @@ def _box_sigma_diagnostics(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() @@ -291,7 +291,7 @@ def _box_topography_bdl2(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() diff --git a/tests/test_1017_custom_mg_stokes.py b/tests/test_1017_custom_mg_stokes.py index bab16eb00..a3c2f4a72 100644 --- a/tests/test_1017_custom_mg_stokes.py +++ b/tests/test_1017_custom_mg_stokes.py @@ -141,10 +141,10 @@ def test_custom_fmg_stokes_constrained(): s.constitutive_model = uw.constitutive_models.ViscousFlowModel s.constitutive_model.Parameters.shear_viscosity_0 = sol.fn_viscosity s.bodyforce = sol.fn_bodyforce - s.add_constraint_bc("Left", g=0.0, normal=sympy.Matrix([[-1.0, 0.0]])) - s.add_constraint_bc("Right", g=0.0, normal=sympy.Matrix([[1.0, 0.0]])) - s.add_constraint_bc("Bottom", g=0.0, normal=sympy.Matrix([[0.0, -1.0]])) - s.add_constraint_bc("Top", g=0.0, normal=sympy.Matrix([[0.0, 1.0]])) + s.add_constraint_bc(0.0, "Left", normal=sympy.Matrix([[-1.0, 0.0]])) + s.add_constraint_bc(0.0, "Right", normal=sympy.Matrix([[1.0, 0.0]])) + s.add_constraint_bc(0.0, "Bottom", normal=sympy.Matrix([[0.0, -1.0]])) + s.add_constraint_bc(0.0, "Top", normal=sympy.Matrix([[0.0, 1.0]])) s.petsc_use_pressure_nullspace = True s.tolerance = 1.0e-9 s.petsc_options["snes_type"] = "ksponly" diff --git a/tests/test_1018_rotated_freeslip.py b/tests/test_1018_rotated_freeslip.py index 0ad51fc85..669880ff7 100644 --- a/tests/test_1018_rotated_freeslip.py +++ b/tests/test_1018_rotated_freeslip.py @@ -56,7 +56,7 @@ def test_rotated_freeslip_box_reproduces_essential(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() @@ -89,8 +89,8 @@ def test_rotated_freeslip_spherical_shell_3d(): g = (r - RI) * (RO - r) * 20.0 s.bodyforce = ylm * g / r * sympy.Matrix([[x, y, z]]) nhat = sympy.Matrix([[x / r, y / r, z / r]]) - s.add_rotated_freeslip_bc("Lower", normal=nhat) - s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.add_rotated_freeslip_bc(0, "Lower", normal=nhat) + s.add_rotated_freeslip_bc(0, "Upper", normal=nhat) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.tolerance = 1e-7 @@ -136,8 +136,8 @@ def test_rotated_freeslip_annulus_zero_leakage(): s.bodyforce = sympy.Matrix([[x / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0, y / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0]]) nhat = sympy.Matrix([[x / r, y / r]]) - s.add_rotated_freeslip_bc("Lower", normal=nhat) - s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.add_rotated_freeslip_bc(0, "Lower", normal=nhat) + s.add_rotated_freeslip_bc(0, "Upper", normal=nhat) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() @@ -179,7 +179,7 @@ def test_rotated_freeslip_geometric_fmg_velocity_block(): s.bodyforce = sol.fn_bodyforce s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" custom_mg.set_custom_fmg(s, coarse, builder="barycentric", field_id=0) @@ -212,7 +212,7 @@ def test_rotated_freeslip_boundary_normal_traction_solcx(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() @@ -247,7 +247,7 @@ def test_rotated_freeslip_sigma_nn_lumped_no_overshoot(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() @@ -322,7 +322,7 @@ def test_rotated_freeslip_nonlinear_matches_essential(plaw_box_ref): mesh, vE, _ = plaw_box_ref s, vR, pR = _powerlaw_stokes(mesh, "nlP") # default (Picard) tangent for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.solve() info = s._rotated_freeslip_info assert info["nonlinear_iterations"] > 1, "rotated solve did not genuinely iterate" @@ -344,7 +344,7 @@ def test_rotated_freeslip_newton_tangent(plaw_box_ref): mesh, vE, ess_its = plaw_box_ref s, vR, pR = _powerlaw_stokes(mesh, "ntR", cj=True) for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.solve() its = s._rotated_freeslip_info["nonlinear_iterations"] assert its <= 2 * ess_its + 2, ( @@ -362,7 +362,7 @@ def test_rotated_freeslip_continuation_tangent(plaw_box_ref): mesh, vE, _ = plaw_box_ref s, vR, pR = _powerlaw_stokes(mesh, "ctR", cj="continuation") for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.solve() info = s._rotated_freeslip_info assert info["continuation_switched"], "continuation never switched Picard→Newton" @@ -372,7 +372,7 @@ def test_rotated_freeslip_continuation_tangent(plaw_box_ref): # picard=N holds the α=0 (Picard) phase for >= N iterations before switching s2, _, _ = _powerlaw_stokes(mesh, "ctR2", cj="continuation") for wall in ("Top", "Bottom", "Left", "Right"): - s2.add_rotated_freeslip_bc(wall) + s2.add_rotated_freeslip_bc(0, wall) s2.solve(picard=25) assert s2._rotated_freeslip_info["nonlinear_iterations"] >= 25, ( "picard did not extend the continuation Picard phase") @@ -386,7 +386,7 @@ def test_rotated_freeslip_picard_newton_unsupported_raises(plaw_box_ref): mesh, _, _ = plaw_box_ref s, v, p = _powerlaw_stokes(mesh, "prN", cj=True) for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) with pytest.raises(NotImplementedError, match="continuation"): s.solve(picard=3) @@ -398,7 +398,7 @@ def test_rotated_freeslip_nonlinear_warm_start(plaw_box_ref): mesh, vE, _ = plaw_box_ref s, vR, pR = _powerlaw_stokes(mesh, "wsR", cj=True) # Newton (fast) for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.solve() # cold s.solve(zero_init_guess=False) # warm (step 2) assert np.linalg.norm(vR.data - vE) / np.linalg.norm(vE) < 1e-6 @@ -421,7 +421,7 @@ def test_rotated_freeslip_nonlinear_geometric_fmg(): s, vR, pR = _powerlaw_stokes(fine, "nlfR", cj=True) for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) custom_mg.set_custom_fmg(s, coarse, builder="barycentric", field_id=0) s.solve() @@ -454,8 +454,8 @@ def test_rotated_freeslip_nonlinear_annulus_zero_leakage(): y / r * sympy.cos(4 * th) * (r - RI) * (RO - r) * 40.0]]) nhat = sympy.Matrix([[x / r, y / r]]) s.consistent_jacobian = True # Newton tangent (few iterations) - s.add_rotated_freeslip_bc("Lower", normal=nhat) - s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.add_rotated_freeslip_bc(0, "Lower", normal=nhat) + s.add_rotated_freeslip_bc(0, "Upper", normal=nhat) s.petsc_use_pressure_nullspace = True s.tolerance = 1e-7 s.solve() @@ -491,7 +491,7 @@ def test_rotated_freeslip_dynamic_topography_field(): s.penalty = 0.0 s.tolerance = 1e-9 for wall in ("Top", "Bottom", "Left", "Right"): - s.add_rotated_freeslip_bc(wall) + s.add_rotated_freeslip_bc(0, wall) s.petsc_use_pressure_nullspace = True s.petsc_options["snes_type"] = "ksponly" s.solve() diff --git a/tests/test_1060_nitsche_freeslip.py b/tests/test_1060_nitsche_freeslip.py index aba90416d..fe436d00d 100644 --- a/tests/test_1060_nitsche_freeslip.py +++ b/tests/test_1060_nitsche_freeslip.py @@ -67,8 +67,8 @@ def _solve_freeslip_box(method, res=8): stokes.add_natural_bc(1e4 * Gamma.dot(v.sym) * Gamma, "Top") stokes.add_natural_bc(1e4 * Gamma.dot(v.sym) * Gamma, "Bottom") elif method == "nitsche": - stokes.add_nitsche_bc("Top", gamma=10.0) - stokes.add_nitsche_bc("Bottom", gamma=10.0) + stokes.add_nitsche_bc(0.0, "Top", gamma=10.0) + stokes.add_nitsche_bc(0.0, "Bottom", gamma=10.0) else: raise ValueError(f"Unknown method: {method}") diff --git a/tests/test_1061_constrained_freeslip.py b/tests/test_1061_constrained_freeslip.py index a68f8b55a..3d0b01a73 100644 --- a/tests/test_1061_constrained_freeslip.py +++ b/tests/test_1061_constrained_freeslip.py @@ -58,8 +58,8 @@ def forcing(m): blk.constitutive_model.Parameters.shear_viscosity_0 = MU blk.bodyforce = forcing(mb) blk.add_dirichlet_bc((0.0, 0.0), "Bottom") - hL = blk.add_constraint_bc("Left", g=0.0, normal=sympy.Matrix([[-1.0, 0.0]])) - hR = blk.add_constraint_bc("Right", g=0.0, normal=sympy.Matrix([[1.0, 0.0]])) + hL = blk.add_constraint_bc(0.0, "Left", normal=sympy.Matrix([[-1.0, 0.0]])) + hR = blk.add_constraint_bc(0.0, "Right", normal=sympy.Matrix([[1.0, 0.0]])) _direct(blk) blk.solve() return dict(ref=ref, blk=blk, hL=hL, hR=hR) @@ -102,7 +102,7 @@ def test_multiplier_and_topography_api(box): def test_rejects_unknown_boundary(box): with pytest.raises(ValueError): - box["blk"].add_constraint_bc("Nonexistent") + box["blk"].add_constraint_bc(0.0, "Nonexistent") # --------------------------------------------------------------------------- # @@ -133,7 +133,7 @@ def annulus(): blk.constitutive_model.Parameters.shear_viscosity_0 = MU blk.bodyforce = buoy * unit_r blk.add_dirichlet_bc((0.0, 0.0), "Lower") - hb = blk.add_constraint_bc("Upper", g=0.0, normal=unit_r) + hb = blk.add_constraint_bc(0.0, "Upper", normal=unit_r) # Enclosed -> pressure/multiplier gauge; DEFAULT grouped-Schur solver config. blk.petsc_use_pressure_nullspace = True blk.tolerance = 1e-8 @@ -208,8 +208,8 @@ def forcing(m): blk.constitutive_model.Parameters.shear_viscosity_0 = visc(mb) blk.bodyforce = forcing(mb) blk.add_dirichlet_bc((0.0, 0.0), "Bottom") - blk.add_constraint_bc("Left", g=0.0, normal=sympy.Matrix([[-1.0, 0.0]])) - blk.add_constraint_bc("Right", g=0.0, normal=sympy.Matrix([[1.0, 0.0]])) + blk.add_constraint_bc(0.0, "Left", normal=sympy.Matrix([[-1.0, 0.0]])) + blk.add_constraint_bc(0.0, "Right", normal=sympy.Matrix([[1.0, 0.0]])) _direct(blk) blk.solve() @@ -244,7 +244,7 @@ def contrast_annulus(): blk.add_dirichlet_bc((0.0, 0.0), "Lower") # reduced augmentation (1e2, vs the 1e4 default): only viable with a Schur PC # that conditions the constraint block — here the default `selfp`. - hb = blk.add_constraint_bc("Upper", g=0.0, normal=unit_r, augmentation_base=1.0e2) + hb = blk.add_constraint_bc(0.0, "Upper", normal=unit_r, augmentation_base=1.0e2) blk.petsc_use_pressure_nullspace = True blk.tolerance = 1e-8 blk.petsc_options["snes_linesearch_type"] = "basic" diff --git a/tests/test_1062_constrained_solcx.py b/tests/test_1062_constrained_solcx.py index dce49c510..3079bb4df 100644 --- a/tests/test_1062_constrained_solcx.py +++ b/tests/test_1062_constrained_solcx.py @@ -29,10 +29,10 @@ def test_constrained_solcx_matches_analytic(): s.constitutive_model.Parameters.shear_viscosity_0 = sol.fn_viscosity s.bodyforce = sol.fn_bodyforce # free-slip on all four walls via in-saddle multipliers - s.add_constraint_bc("Left", g=0.0, normal=sympy.Matrix([[-1.0, 0.0]])) - s.add_constraint_bc("Right", g=0.0, normal=sympy.Matrix([[ 1.0, 0.0]])) - s.add_constraint_bc("Bottom", g=0.0, normal=sympy.Matrix([[ 0.0, -1.0]])) - s.add_constraint_bc("Top", g=0.0, normal=sympy.Matrix([[ 0.0, 1.0]])) + s.add_constraint_bc(0.0, "Left", normal=sympy.Matrix([[-1.0, 0.0]])) + s.add_constraint_bc(0.0, "Right", normal=sympy.Matrix([[ 1.0, 0.0]])) + s.add_constraint_bc(0.0, "Bottom", normal=sympy.Matrix([[ 0.0, -1.0]])) + s.add_constraint_bc(0.0, "Top", normal=sympy.Matrix([[ 0.0, 1.0]])) s.petsc_use_pressure_nullspace = True s.tolerance = 1.0e-9 s.solve() diff --git a/tests/test_1064_constrained_spherical_shell_response.py b/tests/test_1064_constrained_spherical_shell_response.py index 72af086b7..7e3a0edbf 100644 --- a/tests/test_1064_constrained_spherical_shell_response.py +++ b/tests/test_1064_constrained_spherical_shell_response.py @@ -101,19 +101,19 @@ def solve_response(method, solver_mode): stokes.add_natural_bc(y_l0 * unit_r, mesh.boundaries.Internal.name) if method == "nitsche": - stokes.add_nitsche_bc("Upper", normal=unit_r, gamma=NITSCHE_GAMMA) - stokes.add_nitsche_bc("Lower", normal=-unit_r, gamma=NITSCHE_GAMMA) + stokes.add_nitsche_bc(0.0, "Upper", normal=unit_r, gamma=NITSCHE_GAMMA) + stokes.add_nitsche_bc(0.0, "Lower", normal=-unit_r, gamma=NITSCHE_GAMMA) else: stokes.add_constraint_bc( + 0.0, "Upper", - g=0.0, normal=unit_r, augmentation_base=1.0e4, degree=2, ) stokes.add_constraint_bc( + 0.0, "Lower", - g=0.0, normal=-unit_r, augmentation_base=1.0e4, degree=2, diff --git a/tests/test_1065_nitsche_local_h.py b/tests/test_1065_nitsche_local_h.py index 31484294d..15c0bcb3b 100644 --- a/tests/test_1065_nitsche_local_h.py +++ b/tests/test_1065_nitsche_local_h.py @@ -241,8 +241,8 @@ def _solve_freeslip(mesh, method, gamma=10.0): stokes.add_dirichlet_bc((sympy.oo, 0.0), "Bottom") else: local = method == "nitsche_local" - stokes.add_nitsche_bc("Top", gamma=gamma, local_h=local) - stokes.add_nitsche_bc("Bottom", gamma=gamma, local_h=local) + stokes.add_nitsche_bc(0.0, "Top", gamma=gamma, local_h=local) + stokes.add_nitsche_bc(0.0, "Bottom", gamma=gamma, local_h=local) stokes.tolerance = 1e-8 stokes.petsc_options["snes_type"] = "ksponly" stokes.petsc_options["ksp_type"] = "fgmres" diff --git a/tests/test_1065_rotation_gauge_freeslip.py b/tests/test_1065_rotation_gauge_freeslip.py index a7ac37064..3993aff7a 100644 --- a/tests/test_1065_rotation_gauge_freeslip.py +++ b/tests/test_1065_rotation_gauge_freeslip.py @@ -45,8 +45,8 @@ def _freeslip_rotation_coefficient(): st.petsc_use_nullspace = True # build the rotation (+ pressure) nullspace # Fully free-slip: BOTH boundaries are constraint BCs, NO essential velocity # BC -> the rigid-rotation nullspace is ACTIVE (the case the fix targets). - st.add_constraint_bc("Upper", g=0.0, normal=unit_r) - st.add_constraint_bc("Lower", g=0.0, normal=unit_r) + st.add_constraint_bc(0.0, "Upper", normal=unit_r) + st.add_constraint_bc(0.0, "Lower", normal=unit_r) # purely radial drive: does zero work against a rigid rotation, so the # physical solution has no net rotation component. st.bodyforce = 1.0e2 * sympy.sin(3 * sympy.atan2(X[1], X[0])) * unit_r