Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d7d25c9
feat(solvers): custom geometric-MG prolongation hook (scalar)
lmoresi Jun 28, 2026
76298f3
feat(custom_mg): Layer-1 generalized FMG hierarchy (BC-per-level) + s…
lmoresi Jun 28, 2026
6eb0b34
docs(custom_mg): Layer-1 status + remaining hardening before merge
lmoresi Jun 28, 2026
2dc60e3
feat(custom_mg): Stokes velocity-block custom-P FMG (Phase 1, Step 1)
lmoresi Jun 29, 2026
cb77289
docs(custom_mg): mark Stokes velocity-block integration done (Phase 1…
lmoresi Jun 29, 2026
e0181ba
refactor(custom_mg): drop level_solver_factory (Phase 1, Step 2)
lmoresi Jun 29, 2026
2e2c4f9
docs(custom_mg): mark factory removal done (Phase 1, Step 2)
lmoresi Jun 29, 2026
ecabfed
feat(custom_mg): loud serial-only guard for np>1 (Phase 1, Step 3 par…
lmoresi Jun 29, 2026
caff202
docs(custom_mg): record np>1 serial guard (Phase 1, Step 3)
lmoresi Jun 29, 2026
19b778c
feat(custom_mg): parallel (np>1) custom-P FMG, nested co-partitioned …
lmoresi Jun 29, 2026
af8e734
docs(custom_mg): record parallel np>1 done (Phase 1, Step 3)
lmoresi Jun 29, 2026
3686b7b
feat(custom_mg): cover all solver families (SNES_Vector hook; Vector …
lmoresi Jun 29, 2026
32fec5a
docs(custom_mg): solver-family coverage matrix + constrained-parallel…
lmoresi Jun 29, 2026
d7a4ca1
docs(custom_mg): link constrained-parallel segfault to issue #291
lmoresi Jun 29, 2026
932e8d7
docs(layer2): SBR adapt-on-top design (mesh-owned custom-P hierarchy)
lmoresi Jun 29, 2026
e6b4c09
docs(layer2): record adapt-on-top prototype validation (serial + np=2)
lmoresi Jun 29, 2026
6d6d85e
fix(custom_mg): wire assembled Jacobian into KSP before velocity-bloc…
lmoresi Jun 29, 2026
0efdd1c
Merge branch 'feature/custom-mg-prolongation' into feature/adapt-on-top
lmoresi Jun 29, 2026
fa082c2
feat(layer2): mesh.adapt SBR adapt-on-top returns a refined child
lmoresi Jun 29, 2026
b70dbc7
test(layer2): Stokes velocity-block FMG auto-pickup on an adapt() child
lmoresi Jun 29, 2026
13a1a2e
feat(layer2): one MG level per SBR refinement step (not a single jump)
lmoresi Jun 29, 2026
c0296b6
docs+test(layer2): funnel is metric-shape, not node_budget; document …
lmoresi Jun 29, 2026
4fd7359
docs(layer2): design note for newest-vertex bisection (graded adapt-o…
lmoresi Jun 30, 2026
7552dbd
docs(layer2): NVB note — interior-interface intuition + 3D generalisa…
lmoresi Jun 30, 2026
bd0328b
feat(layer2): NVB serial core prototype — graded AMR proven
lmoresi Jun 30, 2026
e097fef
feat(layer2): NVB prototype — track per-cell refinement depth
lmoresi Jun 30, 2026
4b1ffd8
docs(layer2): NVB parallel — decomposition preservation decides Route…
lmoresi Jun 30, 2026
f4895ab
feat(layer2): NVB graded adapt engine — serial Route A in src
lmoresi Jun 30, 2026
01d1cc0
test+docs(layer2): NVB graded adapt tests + design-note status
lmoresi Jun 30, 2026
0287b0b
docs(layer2): NVB parallel-readiness review — serial Route A is non-b…
lmoresi Jun 30, 2026
a214de2
docs(layer2): de-risk the parallel NVB closure-fixpoint (Route B kernel)
lmoresi Jun 30, 2026
4e8772e
docs(layer2): Route B implementation plan — feasibility established
lmoresi Jun 30, 2026
05c9661
feat(layer2): Route B infra gate — native uwnvb DMPlexTransform regis…
lmoresi Jun 30, 2026
5cd9ceb
docs(layer2): settle + prove the Route B build model (headers + binar…
lmoresi Jun 30, 2026
8d7544a
feat(layer2/nvb): native uwnvb DMPlexTransform reproduces refine_sbr …
lmoresi Jul 1, 2026
fc94499
feat(layer2/nvb): graded NVB via single-bisection multi-pass native t…
lmoresi Jul 1, 2026
be9f41c
feat(layer2/nvb): SF-propagated cross-rank closure for parallel NVB
lmoresi Jul 1, 2026
31091e5
fix(layer2/nvb): parallel confluence — OR-reconcile the per-pass bise…
lmoresi Jul 1, 2026
d378197
test(layer2/nvb): skip serial NVBMesh cross-checks under np>1
lmoresi Jul 1, 2026
a5f45df
feat(layer2/nvb): Stage 2c — wire native uwnvb transform into mesh.ad…
lmoresi Jul 1, 2026
05d4d77
docs(layer2/nvb): mark Stage 2c (parallel adapt wiring) done
lmoresi Jul 1, 2026
cbfd64e
test(layer2/nvb): parallel SolCx Stokes FMG acceptance on the NVB child
lmoresi Jul 1, 2026
c16e60d
feat(layer2/adapt): accept an analytic metric in _adapt_nested (fixes…
lmoresi Jul 1, 2026
1d5b7c0
feat(layer2/adapt): callable exact-distance metric, resolved on each …
lmoresi Jul 1, 2026
6fcbed4
refactor(layer2/adapt): normalise the metric to one callable; fn.eval…
lmoresi Jul 1, 2026
29ab552
feat(surfaces): Surface.remap_to(mesh) + director property for adapt-…
lmoresi Jul 2, 2026
b7377ea
Merge remote-tracking branch 'origin/development' into feature/adapt-…
lmoresi Jul 2, 2026
a4add4b
fix(custom_mg): don't auto-inject velocity-block FMG onto scalar solv…
lmoresi Jul 2, 2026
1e1fabd
refactor(custom_mg): rename maybe_inject_custom_mg -> auto_inject_cus…
lmoresi Jul 2, 2026
29890cd
Merge branch 'feature/adapt-on-top' into bugfix/custom-mg-parallel
lmoresi Jul 3, 2026
5ac985e
fix(custom_mg): operator-faithful finest reduced map on adapt children
lmoresi Jul 3, 2026
7311fe6
feat(custom_mg): cross-partition transfer for non-nested coarse tails…
lmoresi Jul 3, 2026
8576118
Merge origin/development into feature/land-custom-mg-parallel
lmoresi Jul 16, 2026
e79324d
style: state the sanctioned failure mode on the custom_mg setUp swallow
lmoresi Jul 16, 2026
4bb80ad
docs: add the two new adapt design docs to the Design Documents toctree
lmoresi Jul 16, 2026
01e9972
fix(adapt): guard the legacy in-place mesh.adapt(metric) call shape
lmoresi Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/advanced/mesh-adaptation.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ metric = uw.adaptivity.metric_from_gradient(
)

# Adapt the mesh
mesh.adapt(metric)
mesh.remesh(metric)
```

## Core Concepts
Expand Down Expand Up @@ -65,7 +65,7 @@ for each edge vector $\mathbf{e}$. Edges that are too long get subdivided; regio
UW3 offers **two complementary** ways to put resolution where it is
needed:

| | `mesh.adapt(...)` (this page) | `smooth_mesh_interior(method="anisotropic")` |
| | `mesh.remesh(...)` (this page) | `smooth_mesh_interior(method="anisotropic")` |
|---|---|---|
| Mechanism | **Re-mesh** (MMG): insert/remove/retriangulate | **Redistribute** the existing nodes (move only) |
| Node budget | *Changes* — targets an **absolute** edge length `h` | **Fixed** — relative redistribution to a target *density* |
Expand All @@ -75,7 +75,7 @@ needed:
| Cost | Re-mesh + full variable transfer | A few cheap SPD elliptic solves (no re-mesh) |
| Parallel | MMG re-partition | O(N), GAMG-parallelisable, no transfer |

Use `mesh.adapt` when you need a genuinely finer mesh (more
Use `mesh.remesh` when you need a genuinely finer mesh (more
elements) and can afford to rebuild the problem. Use the
**node-snuggling** redistribution when you want to *reshape* the
existing mesh toward a feature every timestep cheaply, keeping the
Expand Down Expand Up @@ -241,7 +241,7 @@ metric = fault.refinement_metric(
)

# Adapt mesh
mesh.adapt(metric)
mesh.remesh(metric)

# Check result
print(f"Adapted mesh: {mesh.X.coords.shape[0]} nodes")
Expand All @@ -258,7 +258,7 @@ p = uw.discretisation.MeshVariable("p", mesh, 1, degree=1)

### Variables Are Reset

After `mesh.adapt()`, all variables on the old mesh become invalid. Variables on the new mesh start uninitialized.
After `mesh.remesh()`, all variables on the old mesh become invalid. Variables on the new mesh start uninitialized.

**For analytical initialization:**
```python
Expand Down Expand Up @@ -347,7 +347,7 @@ pixi run -e amr python -c "import underworld3; print('AMR ready')"

| Function | Purpose |
|----------|---------|
| `mesh.adapt(metric)` | Adapt mesh using metric field |
| `mesh.remesh(metric)` | Re-mesh (in place) using metric field |
| `uw.adaptivity.create_metric(mesh, h)` | Convert h-field to metric |
| `uw.adaptivity.metric_from_gradient(field, ...)` | Metric from field gradient |
| `uw.adaptivity.metric_from_field(indicator, ...)` | Metric from indicator field |
Expand Down Expand Up @@ -380,7 +380,7 @@ For the mathematically inclined, see the [Developer Design Document](../develope
When you want to concentrate resolution on an evolving feature
**every timestep** without re-meshing — keeping the topology and
all field data intact — use `smooth_mesh_interior` (the node-moving
mover) instead of `mesh.adapt`:
mover) instead of `mesh.remesh`:

```python
import underworld3 as uw
Expand Down
38 changes: 35 additions & 3 deletions docs/developer/design/GENERALIZED_FMG_HIERARCHY_AND_ADAPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,45 @@ The supported parallel path is the **nested, co-partitioned** hierarchy:
(`localToGlobal` gives the reduced global ordering across ranks).
- `P` is assembled as an MPIAIJ matrix (fine local rows; coarse local + ghost columns).

**Non-nested / independent-mesh custom-P** (cross-rank point-location) is **serial-only /
experimental** — it is not on the parallel-supported path. The production case
(targeted refinement = nested) needs only the rank-local path.
**Non-nested / independent-mesh custom-P** in parallel (cross-rank point location) is now
supported via a **cross-partition transfer** (`_build_crosspart_transfer`,
`_gather_coarse_cloud`). When the coarse and fine meshes are partitioned independently, a
fine leaf on rank *r* can sit in a coarse cell owned by rank *s*, so the rank-local builder
either misses it (nearest-DOF fallback — wrong) or leaves a coarse DOF with no fine image
(zero column). The fix exploits the fact that a coarse MG level is, by definition, **small**:

- **All-gather the coarse node cloud** (coords + each node/component's GLOBAL reduced column
index, `-1` for a BC-constrained DOF), deduplicated by rounded coordinate (ghost copies are
bit-identical). Every rank then holds the *full* coarse mesh.
- Each rank locates its **owned** fine nodes against that full cloud → point location spans
partitions. Columns are the coarse global reduced indices (off-rank columns are fine for
MPIAIJ); constrained coarse DOFs stay as barycentric vertices but drop from the columns
(reduced→reduced). Fine rows stay rank-local.

`CustomMGHierarchy(..., cross_partition=...)` / `set_custom_fmg(..., cross_partition=...)`:
`"auto"` (default) builds the rank-local co-partitioned transfer first and rebuilds a level
cross-partition **only if it has zero columns** (the signature of a cross-partition miss) —
so the validated nested/adapt path stays on the fast rank-local builder bit-for-bit, while
non-nested tails are fixed automatically. `True` forces cross-partition (use when a coarse
level is known non-nested and might mis-locate without producing a zero column); `False`
forces the rank-local path. Validated: an independent (non-co-partitioned) coarse box tail
converges in the same iteration count as serial and matches a GAMG reference to ~1e-8 at
np2 and np4.

Non-load-balancing SBR → bound the added refinement depth (configurable cap); document the
imbalance/level trade-off.

### Operator-faithful finest reduced map
The finest transfer's row space **must** equal the assembled operator's space (PCMG Galerkins
`PᵀAP` against the real operator). The finest reduced map is read from the DM global section,
which is that space only **after** `snes.setUp()` finalizes it — critical on an `adapt()`
child, whose section can otherwise be read before finalization and disagree with the operator
(a rectangular finest transfer → bare PETSc error 60 in the PtAP). `CustomMGHierarchy.build`
calls `snes.setUp()` before reading the finest map and asserts its size against the assembled
operator (`_assert_finest_matches_operator`), failing with an actionable message instead. This
removed the earlier defensive skip of semi-Lagrangian advection-diffusion on adapt children —
such solves now install custom-P geometric MG and match a default-preconditioner solve.

## Correctness invariants (must hold, all levels)
1. BCs applied at every level; transfers reduced→reduced (no zero columns).
2. Each prolongation reproduces constants (row-sums = 1 — partition of unity).
Expand Down
Loading
Loading