Skip to content

Commit 9cb7f34

Browse files
Sébastien LoiselSébastien Loisel
authored andcommitted
Add embedded manifolds: curves in R^2/R^3 and surfaces in R^3
Generalize the tensor-product FEM from codimension-0 domains to embedded manifolds, with a single code path that reduces exactly to the old behavior. - TensorFEM gains an ambient-dimension type parameter: TensorFEM{d,e,T} (intrinsic dim d, ambient/embedding dim e >= d). fem1d/fem2d take ambient=Val(e) (default Val(d)) so constructor return types stay inferrable; fem3d is unchanged (e=3 forced). Aliases FEM1D/FEM2D/FEM3D fix only d. - The operators :dx,:dy,:dz are the ambient components of the intrinsic (tangential) gradient, computed via the Jacobian pseudo-inverse g\J' with g = J'J the first fundamental form; quadrature weights use the surface / arc-length measure sqrt(det g). At e=d this is inv(J) and |det J|, so codim-0 results are unchanged within roundoff. No normal vector is ever needed. - amg: the auxiliary Dirichlet energy now sums all e ambient gradient components (the full tangential energy), not just d. - PyVista plotting for surfaces (TensorFEM{2,3}) and curves (TensorFEM{1,e}): graph the solution into the first free dimension when e<3 (a planar curve is drawn as the height-graph (x,y,u)), color it in place when e=3. - test/test_manifold.jl: scalar p-Laplace + mass solves on a circle (R^2 and R^3) and a sphere, validated against manufactured Laplace-Beltrami eigenfunctions and by embedding-independence to machine precision, plus surface/curve plotting smoke tests. - docs: new "Embedded manifolds" section with a runnable example; front-end table and module/struct docstrings updated.
1 parent e1246e7 commit 9cb7f34

8 files changed

Lines changed: 488 additions & 105 deletions

File tree

docs/src/index.md

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,51 @@ A time-dependent 3D problem:
166166
plot(parabolic_solve(amg(subdivide(fem3d(; k=1), 2)); h=0.1, verbose=false))
167167
```
168168

169+
## Embedded manifolds
170+
171+
`fem1d` and `fem2d` also build **embedded manifolds** — a curve or surface living
172+
in a higher-dimensional ambient space — when you pass `ambient=Val(e)` together with
173+
a mesh `K` whose third dimension has `e ≥ d` coordinate columns. The cases are a
174+
curve in ℝ² or ℝ³ (`fem1d`, `e = 2` or `3`) and a surface in ℝ³ (`fem2d`, `e = 3`);
175+
the discretization type carries both dimensions as `TensorFEM{d,e,T}` (e.g.
176+
`TensorFEM{2,3,Float64}` is a surface in ℝ³).
177+
178+
On an embedded manifold the operators `:dx, :dy[, :dz]` are the `e` ambient
179+
components of the **intrinsic (tangential) gradient**_Γ — each output vector is
180+
tangent to the manifold, so `n · ∇_Γ u = 0` holds by construction and no normal
181+
vector is ever needed (which keeps higher codimension well-defined). The quadrature
182+
weights `geom.w` are the induced surface / arc-length measure `√det(JᵀJ)`. Closed
183+
manifolds (circles, spheres, tori) close up automatically when coincident element
184+
corners are deduplicated; pass `t` explicitly for a glued/periodic mesh.
185+
186+
Everything downstream is unchanged: `amg(geom)`, `assemble`, and `mgb_solve` work as
187+
usual. The only adjustment when posing a problem is that the operator matrix `D`
188+
must list **all `e` ambient gradient components** (e.g. `:dx :dy :dz` for a surface),
189+
since `default_D` keys on the intrinsic dimension; see `test/test_manifold.jl` for a
190+
worked scalar `p`-Laplace-plus-mass solve on a circle and a sphere.
191+
192+
Plotting follows one rule — *graph the solution into the first free dimension, else
193+
show it as color*: a curve in ℝ² is drawn as the height-graph `(x, y, u)`, a curve
194+
in ℝ³ as a tube colored by `u`, and a surface in ℝ³ as a colored surface. For
195+
example, `cos 2θ` on the unit circle:
196+
197+
```@example 1
198+
N = 120
199+
θ = range(0, 2π, length=N+1)
200+
K = zeros(2, N, 2)
201+
for e in 1:N
202+
K[1, e, :] = [cos(θ[e]), sin(θ[e])]
203+
K[2, e, :] = [cos(θ[e+1]), sin(θ[e+1])]
204+
end
205+
circle = fem1d(; K = K, ambient = Val(2)) # a curve in ℝ²: TensorFEM{1,2}
206+
u = [cos(2 * atan(circle.x[v, e, 2], circle.x[v, e, 1]))
207+
for e in 1:size(circle.x, 2) for v in 1:size(circle.x, 1)]
208+
fig = plot(circle, u)
209+
savefig(fig, "manifold_circle.png"); nothing # hide
210+
```
211+
212+
![](manifold_circle.png)
213+
169214
## Front-end summary
170215

171216
The mesh constructors below all return a single-level `Geometry`. Boundary conditions
@@ -175,8 +220,8 @@ node sets for mixed and per-component conditions.
175220

176221
| Function | Element | Dim | Key kwargs |
177222
| --- | --- | --- | --- |
178-
| `fem1d` | `Q_k` interval (P1 at `k=1`) | 1D | `nodes` (or `K`), `k` (defaulted) |
179-
| `fem2d` | `Q_k` quadrilaterals | 2D | `K`, `k` (defaulted) |
223+
| `fem1d` | `Q_k` interval (P1 at `k=1`) | 1D (curve in 2D/3D) | `nodes` (or `K`), `k`, `ambient` (defaulted) |
224+
| `fem2d` | `Q_k` quadrilaterals | 2D (surface in 3D) | `K`, `k`, `ambient` (defaulted) |
180225
| `fem2d_P1` | P1 triangles | 2D | `K` (defaulted) |
181226
| `fem2d_P2` | P2 + cubic bubble triangles | 2D | `K` (defaulted) |
182227
| `fem3d` | `Q_k` hexahedra | 3D | `K`, `k` (defaulted) |
@@ -247,7 +292,11 @@ All FEM constructors take their coordinates as a `K` keyword argument
247292
straight elements, which is promoted internally). For the simplicial family
248293
`V` is 3 for `fem2d_P1` and 7 for `fem2d_P2`;
249294
- `N` is the number of elements;
250-
- `D` is the spatial dimension (1, 2, or 3).
295+
- `D` is the **ambient** (embedding) dimension — the number of coordinate
296+
components, 1, 2, or 3. For an ordinary codimension-0 mesh it equals the
297+
intrinsic element dimension `d` (1 for `fem1d`, 2 for `fem2d`, 3 for `fem3d`);
298+
for an **embedded manifold** (a curve or surface living in a higher-dimensional
299+
space) it exceeds `d` — see [Embedded manifolds](@ref) below.
251300

252301
`K[v, e, d]` is the `d`-th coordinate of the `v`-th local node of the `e`-th
253302
element. So in 1D, nodes `x[1] < x[2] < … < x[m]` defining elements

src/MultiGridBarrier.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ MultiGridBarrier solves nonlinear convex optimization problems in function space
55
barrier (interior-point) method accelerated by a multigrid hierarchy. The package exposes:
66
77
- Single-level mesh constructors: `fem1d`, `fem2d`, `fem2d_P1`, `fem2d_P2`, `fem3d`, `spectral1d`,
8-
`spectral2d`. Each returns a `Geometry`.
8+
`spectral2d`. Each returns a `Geometry`. `fem1d`/`fem2d` also build embedded manifolds —
9+
a curve or surface in a higher ambient dimension — via `ambient=Val(e)` (intrinsic
10+
gradient operators, surface/arc-length measure); see the manual's "Embedded manifolds".
911
- Topological connectivity for slit domains / glued manifolds: `tensor_dofmap` builds
1012
full-node connectivity from corner connectivity (no coordinates); pass the result as the
1113
`t=` keyword of `fem1d`/`fem2d`/`fem3d` so geometrically-coincident nodes stay distinct.

0 commit comments

Comments
 (0)