Date: 2026-01-20 Purpose: Mathematical foundation and implementation details of field recovery in Maxwell postprocessor Module: src/fem/maxwell
⚠️ AUTHORITATIVE DOCUMENT: This document defines the mathematical model for Maxwell field recovery. Other documents may reference or summarize, but NOT redefine the recovery method.
See Also:
| Symbol | Meaning | Units (SI) | Notes |
|---|---|---|---|
| H | Magnetic field intensity | A/m | Primary variable in h-φ formulation |
| B | Magnetic flux density | T (Tesla) | B = μ·H |
| J | Current density | A/m² | J = ∇×H for conductors |
| Jc | Critical current density | A/m² | Superconductor threshold |
| φ | Scalar magnetic potential | A (Ampere) | In air/ferro regions |
| A | Vector magnetic potential | Wb/m | B = ∇×A (not used in h-φ) |
| μ | Magnetic permeability | H/m | μ = μ₀·μᵣ, nonlinear for ferro/SC |
| μ₀ | Vacuum permeability | 4π×10⁻⁷ H/m | Constant |
| σ | Electrical conductivity | S/m | For Ohmic conductors |
| T | Temperature | K (Kelvin) | For thermal coupling |
| σ* | Recovered field | (varies) | SPR-projected quantity |
| σₕ | Raw FEM field | (varies) | Discontinuous across elements |
| p(x) | Polynomial basis | - | Monomials for recovery |
| c | Polynomial coefficients | - | From least-squares fit |
| V | Gramian/Vandermonde matrix | - | SPD, size m×m |
The Maxwell postprocessor implements a Superconvergent Patch Recovery (SPR) method to project discontinuous element-wise electromagnetic fields onto smooth nodal values suitable for visualization and output. This document explains the mathematical theory, implementation approach, and domain-specific aspects.
Finite element solutions provide continuous primary variables:
However, derived quantities (gradients, curls, material-dependent fields) are:
Recovery goal: Construct smooth, continuous fields with higher accuracy by exploiting superconvergence properties at integration points.
| Method | Description | Accuracy | Cost |
|---|---|---|---|
| Direct averaging | Average element values at nodes | O(h^p) | Very low |
| L² projection | Global least-squares fit | O(h^(p+1)) | High (global solve) |
| SPR (Zienkiewicz-Zhu) | Local polynomial fit to patches | O(h^(p+1)) | Low (local solves) |
| Gradient recovery (ZZ) | SPR + error estimation | O(h^(p+1)) | Medium |
BELFEM uses SPR for optimal balance of accuracy and computational cost.
For each node i, recover a smooth field σ* from discontinuous element values σₕ:
Step 1: Define polynomial approximation
where:
Step 2: Weighted least-squares on element patch
Minimize the error over all integration points in the patch:
This yields the normal equations:
Step 3: Solve and evaluate
The monomials are expressed in local coordinates rather than global coordinates. For each node, the basis is centered at that node and scaled by the patch radius: p( (x − x₀)/h ), where h is the greatest distance from the node to an integration point in its patch. A complete polynomial space of fixed degree is unchanged by an affine coordinate transformation, so centering and scaling affect the recovered value only through round-off. They do, however, improve conditioning. In the rutherford example, the Gramian for a micrometer-thin layer patch located millimeters from the origin had a condition number of about 4e12 in absolute coordinates. After centering and scaling, the condition number was about 4e5, and the result no longer depended on the mesh origin.
Linear thin-shell layer blocks (PENTA6TS, QUAD4TS) use curvilinear coordinates: p = [1, s₁/h, s₂/h, η/τ]ᵀ in 3D and [1, s/h, η/τ]ᵀ in 2D. Here, s₁ and s₂ are the integration point's offsets from the node after projection onto the node's tangent plane. The node normal is the mean of the bottom-to-top directions of the patch elements, and the tangents complete a right-handed frame. The offset η through the stack is computed from the element's thickness coordinate ζ ∈ [−1, 1] and the layer's prescribed thickness t:
τ is the largest |η| in the patch. The value of η is zero at the node in every patch element. It is also signed consistently across a node shared by two layers: negative below the node and positive above it. Thus, the basis is e₀ at the node, and the single η term can represent a profile that is linear through each layer.
This shell basis is BELFEM's extension of the patch fit; it is not part of Zienkiewicz & Zhu
Limit: if a field is linear in η within each of two adjacent layers handled by one postprocessor but has a kink at their interface, the fit still uses one line. Exact recovery is guaranteed only for fields that are linear in (s₁, s₂, η) over the patch.
BELFEM uses complete polynomial spaces matching the element interpolation order:
Implementation: cl_FEM_Postprocessor.hpp:226-366 (inline functions poly1_2d through poly4_3d)
BELFEM uses element volume weighting:
This ensures:
Alternative weights considered but not used:
Code reference: cl_MaxwellPostprocessor.cpp:730, cl_FEM_Postprocessor.cpp:285
A patch consists of all elements surrounding a node:
Basic patch (depth=0):
Extended patch (depth>0):
Code reference: cl_MaxwellPostprocessor.cpp:641-691
Used by: the base Postprocessor::run(), which MaxwellPostprocessor::run() calls first (cl_MaxwellPostprocessor.cpp:388-390) before its own per-type work. The recovery itself lives in Postprocessor::recover_fields(), not in the Maxwell subclass.
Algorithm:
Advantages:
Disadvantages:
Code reference: cl_MaxwellPostprocessor.cpp:636-770
Used by: Parent Postprocessor class (alternative implementation)
Algorithm:
Advantages:
Disadvantages:
Code reference: cl_FEM_Postprocessor.cpp:219-487
Used by: cl_Gradient.cpp (independent implementation)
Algorithm:
Note: Uses gesv (general LU) instead of posv (Cholesky), despite V being SPD. This is functionally correct but suboptimal.
Disadvantages:
Code reference: cl_Gradient.cpp:100-226
Gramian matrix V:
Solve (recovery_block_inverse in src/fem/kernel/fn_FEM_recovery_inverse.hpp, called by Postprocessor::invert_node_matrix): the constant coefficient is eliminated exactly using the Schur complement of V₀₀, which is the patch weight:
S⁺ is the truncated pseudoinverse obtained from the SVD of S (for a symmetric positive semidefinite block, this is its eigen decomposition). Singular values below σ_max / 10¹⁰ are dropped. On a healthy patch, no singular values are dropped, so the result equals the exact inverse. On a degenerate patch, such as one with too few points or coplanar points, undetermined directions are dropped while well-determined directions are retained. A constant field is reproduced exactly because c₀ does not pass through the truncation. The stored node matrix is the block inverse:
The recovered value is the zeroth row of this matrix multiplied by b. A rank on which any patch required truncation prints a warning with their number. A zero weight sum, a non-finite coordinate, a collapsed layer element, or an integration rule without a thickness coordinate is a setup error (BELFEM_ERROR), not a truncation.
The compute() function computes physical quantities from primary DOFs:
| Domain Type | Primary DOFs | Field Computation | Code Reference |
|---|---|---|---|
| Air | φ (nodal) | H = -∇φ = -B(ξ)·φ | compute_air:909-917 |
| Ferro | φ (nodal) | H = -∇φ B = μ(H)·H | compute_ferro:919-932 |
| Conductor | edge_h (Nédélec) | H = E(ξ)·edge_h B = μ(H)·H J = C(ξ)·edge_h = ∇×H | compute_conductor:934-949 |
| Superconductor | edge_h (Nédélec) | H, B, J (as above) J/Jc = J / Jc(B,T,θ) | compute_superconductor:1008-1060 |
| Thin-shell SC | edge_h + φₘ, φₛ | H, B, J, J/Jc (includes normal field from master/slave) | compute_superconductor_ts:1063-1148 |
Key operations:
For precomputed weight optimization, it is critical to know which fields are linear in the DOFs (can be expressed as M · DOFs) and which are nonlinear:
| Domain | Field | Computation | Linear in DOFs? |
|---|---|---|---|
| Air | H | -B(ξ) · φ | Yes |
| Ferro | H | -B(ξ) · φ | Yes |
| Ferro | B | μ(‖H‖) · H | No (μ depends on ‖H‖) |
| Conductor | H | E(ξ) · edge_h | Yes |
| Conductor | B | μ(‖H‖) · H | No |
| Conductor | J | C(ξ) · edge_h | Yes |
| Superconductor | H | E(ξ) · edge_h | Yes |
| Superconductor | B | μ(‖H‖) · H | No |
| Superconductor | J | C(ξ) · edge_h | Yes |
| Superconductor | J/Jc | J / Jc(‖B‖, θ, T) | No |
Consequence: H and J can be recovered using precomputed element-wise weight matrices (see Section 13). B and J/Jc require integration-point-level evaluation due to material nonlinearity.
For ferromagnetic and superconducting materials:
The permeability is evaluated at each integration point:
Thermal coupling: If thermal kernel is active, temperature T is interpolated from thermal DOFs at the same integration point.
Code reference: cl_MaxwellPostprocessor.cpp — the mMaterial->mu( norm( mH ) ) evaluation sites (search mMaterial->mu)
For thin-shell superconductors, the recovery includes:
Normal field contribution:
where φₘ, φₛ are potentials from master/slave elements.
Total field:
Angle for critical current:
This accounts for the angular dependence of critical current in anisotropic superconductors.
Recovery basis: thin-shell layer blocks use the shell coordinates from §2.1.2. This preserves the jump in tangential field across a superconducting layer, including on curved shells. Nodes shared by adjacent blocks are still written by both passes, with the superconductor pass running last (see thinshell_postprocessor_node_sharing.md).
Code reference: cl_MaxwellPostprocessor.cpp — the mJJc /= mMaterial->jc(...) normalization block (search mJJc)
What are T-Matrices?
T-matrices (transformation/constraint matrices) enforce continuity of tangential H-field at mesh refinement interfaces where hanging nodes or hanging facets exist. In h-refined meshes (adaptive refinement), constrained DOFs on fine-mesh boundaries must satisfy compatibility with coarse-mesh DOFs.
Why are they needed?
Nédélec edge elements naturally ensure tangential continuity of H within conforming meshes. However, at hanging-node interfaces (where fine elements meet coarse elements):
Without constraints, the two fine-element edge DOFs are independent, potentially creating discontinuity in tangential H at the interface. This violates Maxwell's equations:
T-matrices enforce this constraint by expressing fine-mesh DOFs as linear combinations of coarse-mesh DOFs.
Mathematical Formulation:
For constrained DOFs (hanging), express them in terms of independent DOFs:
System-level projection:
When assembling stiffness matrix K and RHS f, constrained DOFs are eliminated via:
This reduces the system size and ensures constraints are satisfied automatically.
Maxwell-Specific Implementation:
BELFEM's maxwell::TMatrix class (cl_Maxwell_TMatrix.hpp/cpp) computes constraint coefficients for thin-shell interfaces where master and slave elements meet:
Key operations:
Parent Class: General T-Matrix Storage
The parent class fem::Tmatrix (cl_FEM_Tmatrix.hpp/cpp) stores constraint matrices in CSR (Compressed Sparse Row) format:
Usage in Recovery:
During postprocessing, T-matrices ensure recovered fields respect hanging-node constraints:
When are T-Matrices Created?
T-matrices are precomputed during mesh initialization if:
For uniform meshes without hanging nodes, T-matrices are not needed.
Debugging and Validation:
The Maxwell TMatrix implementation includes assertion checks (cl_Maxwell_TMatrix.cpp:70-79):
Code References:
| File | Purpose | Key Lines |
|---|---|---|
| cl_Maxwell_TMatrix.hpp | Maxwell-specific header | 23-54 |
| cl_Maxwell_TMatrix.cpp | Constraint computation | 43-175 |
| cl_FEM_Tmatrix.hpp | General CSR storage | 22-82 |
| cl_FEM_Tmatrix.cpp | T·v and Tᵀ·A·T operations | (implementation) |
| hangingnodes.cpp | Example usage | 25, 46 |
Literature Reference:
The mathematical foundation for thin-shell interface constraints and cohomology-based cut generation is covered in:
The general theory of hanging-node constraints in FEM is covered in:
Superconvergent Patch Recovery:
Textbook treatment:
Superconvergence theory:
Moving Least Squares (MLS) connection:
Nédélec element postprocessing:
BELFEM implementation papers:
Theorem (Barlow 1976): For polynomial finite elements of degree p, derivatives (gradients, curls) are superconvergent at Gauss integration points:
Consequence: Sampling at integration points and fitting a polynomial of degree p recovers one extra order of accuracy.
Reference: Zienkiewicz Vol. 1, §15.2
Standard FEM:
With SPR:
Practical implication: For linear elements (p=1), gradients improve from O(h) to O(h²).
SPR achieves optimal convergence when:
BELFEM satisfies all conditions for typical electromagnetics meshes.
BELFEM's postprocessor applies two smoothing mechanisms internally (there are no user-callable process_*() entry points — the class interface is run() / initialize(), driven by the controller at every saved timestep):
| Property | Patch recovery (SPR) | Inverse-distance filtering |
|---|---|---|
| Method | Polynomial least-squares (SPR) | Inverse-distance weighted average |
| Accuracy | O(h^(p+1)) | O(h^p) |
| Cost | Medium (small matrix solve) | Low (simple averaging) |
| Smoothness | C⁰ continuous, polynomial | C⁰ continuous, piecewise |
| Use case | Production, accurate visualization | Quick/debug visualization |
| Code | cl_MaxwellPostprocessor.cpp (search patch recovery) | cl_MaxwellPostprocessor.cpp |
Filter formula:
This is Shepard's method (1968), a simple form of Moving Least Squares (MLS) with inverse-distance weighting.
Each MPI rank processes:
Patch assembly:
Phase 1: Node distribution (collect_nodes)
Phase 2: Field computation (run)
Phase 3: Global assembly (collect_fields)
Code reference: cl_MaxwellPostprocessor.cpp:426-500, 374-386
The Zienkiewicz-Zhu (ZZ) error estimator extends SPR:
Estimate local error:
Global error estimate:
This is used for adaptive mesh refinement (h-adaptivity).
Implementation status in BELFEM: Not currently implemented, but infrastructure is in place:
Reference: Zienkiewicz Vol. 1, §15.6
Terminology note: The BELFEM implementation is true SPR (Zienkiewicz-Zhu), not simple L² projection.
This IS the classic SPR algorithm from Zienkiewicz & Zhu (1992).
By design, the following are NOT implemented:
Recovery depth > 0: Use only if Gramian is ill-conditioned at depth 0 (rare). Increases cost exponentially.
If you need these features, consult development team before implementing.
The Maxwell postprocessor implements Zienkiewicz-Zhu Superconvergent Patch Recovery in node-local coordinates, with a curvilinear basis for thin-shell layers:
✅ Exploits superconvergence at Gauss integration points ✅ Uses centered and scaled polynomial fits with weighted least squares (§2.1.1) ✅ Uses shell coordinates (s₁, s₂, η) for PENTA6TS / QUAD4TS layers (§2.1.2) ✅ Solves the constant exactly and truncates only the gradient block with SVD (§3.2); no abort occurs for a degenerate patch ✅ Domain-aware field computation (H, B, J, Jc) ✅ MPI-parallel with local patch assembly
Convergence rate: O(h^(p+1)) for recovered gradients/curls (one order better than raw FEM)
Key references:
For potential GPU acceleration discussion (extreme-scale only), see: ../../../../todo/maxwell_postprocessor_gpu_acceleration.md
Status: Planned. See todo/postprocessor_recovery_findings.md for full analysis, memory estimates, cache invalidation rules, and validation gates.
All three current recovery modes (Sections 3.1) share the same fundamental inefficiency: geometry-dependent work (Vandermonde assembly, Cholesky factorization, coordinate transforms, polynomial evaluation) is repeated every timestep, even though it depends only on static mesh geometry.
Additionally, per-node recovery (Modes 1 and 3) visits each element multiple times — once per adjacent node — multiplying the element loop cost by ~6 in 2D.
The SPR result at node n can be decomposed as:
Let q_n = V_n⁻¹ · p(x_n) (computed via Cholesky factor/solve, not explicit inversion). Since q_n depends only on geometry, it is precomputable.
For linear fields (H, J), where y_{e,ip} = M_e(x_ip) · DOFs_e:
where W_e[n] is a small precomputed matrix per element per node:
Store one matrix W_e per element of size (n_nodes × n_fields) × n_dofs:
Advantages over current implementation:
For nonlinear fields (B, J/Jc), DOF-based weights cannot be used because the field depends nonlinearly on DOFs (see Section 4.1.1).
Chosen approach: Use precomputed scalar alpha weights at the integration-point level for nonlinear fields, preserving numerical equivalence with the current method. See todo/postprocessor_recovery_findings.md for details and acceptance criteria.
| Recovery class | Current mode | Optimization applies? |
|---|---|---|
| MaxwellPostprocessor | Per-node (Mode 1) | Yes — highest impact |
| Postprocessor base | Batch (Mode 2) | Yes |
| Gradient | Per-node (Mode 3) | Yes (M = J⁻¹ · dNdξ) |
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.3 | 2026-03-06 | C. Messe | Fixed Mode 2 description (was overstating advantages). Added Mode 3 (Gradient). Added Section 4.1.1 (linearity analysis). Added Section 13 (element-wise W_e optimization plan). |
| 1.2 | 2026-01-20 | C. Messe | Added Section 4.4: T-Matrix explanation (constraint matrices for hanging nodes) |
| 1.1 | 2026-01-20 | C. Messe | Added cross-references, glossary, SPR clarification, "What NOT to do" section |
| 1.0 | 2026-01-20 | C. Messe (via Claude Code) | Initial comprehensive documentation |