Adaptive scalar and elasticity indicators
Darcy refinement policies, local error controls, metric remeshing and the primal elasticity indicator.
Darcy and transport controllers accept solve_step to assemble each state
from user-defined equations. The callable receives the current mesh and
skeleton with the problem's keyword options; it returns the physical solution
required by the chosen estimator. The default formulation remains available.
The reconstruction's algebraic moment conditions, estimator reliability and
optimal approximation estimates have separate hypotheses. In particular,
the recovery paper's optimal flux estimate assumes polynomial diffusion on
each fine simplex and its stated pressure/flux regularity; exact integration
of a material cut does not establish that fine-simplex material condition.
See Barrenechea et al. (2026) and the
material-weighted estimator for the applicable
norms, geometry, boundary representation and equilibrium conditions.
See the variational guide
for composition with mathematical definition factories.
Estimator-driven conforming macro refinement for primal Darcy problems.
AdaptiveDarcyResult
dataclass
Solved states, estimators, marking decisions and actual refinement maps.
totals: FloatArray
property
Return the selected estimator or numerical indicator at every solved level.
mark_dorfler(local_squared: Any, theta: float = 0.5) -> np.ndarray
Select a minimal-cardinality deterministic bulk set of squared indicators.
The selected sum is at least theta times the total sum. Ties follow
original cell order. Zero indicators produce an empty set. Scaling before
summation avoids overflow and preserves the marking for extreme units.
solve_adaptive_darcy(mesh: TriangleMesh, *, iterations: int = 5, theta: float = 0.5, tolerance: float = 0.0, maximum_cells: int = 10000, trace_degree: int = 0, trace_segments: int = 1, trace_space: FaceSpace | None = None, reconstruction_degree: int = 1, estimator_order: int = 8, estimator_convention: Literal['published', 'energy'] = 'energy', estimator_backend: Literal['serial', 'thread', 'process'] = 'serial', estimator_workers: int | None = None, ellipticity_lower_bound: Any = None, skeleton: SkeletonSpace | None = None, local_mesh_factory: Callable[[TriangleMesh, int], TriangleMesh] | None = None, macro_refiner: Callable[[TriangleMesh, Any], TriangleRefinement] = refine_triangles, solve_step: Callable[..., DarcySolution] | None = None, **problem: Any) -> AdaptiveDarcyResult
Solve, estimate, mark and red-green-refine a primal Darcy macro mesh.
This loop uses the weighted energy decomposition of
Barrenechea et al. (2026) and Dörfler bulk marking; it is
an original adaptive policy, not a reproduction of a published marking schedule. All estimator
assumptions remain required: polynomial represented boundary data, conforming fine
triangulations, certified ellipticity and local degree >= trace degree + 2. Local refinement
does not certify the recovery paper's optimal flux approximation hypotheses: Theorem 4.7
additionally requires polynomial diffusion on each fine triangle, H^(k+1) pressure and
A grad(p) in H^(ell+1) intersected with H(div), with compatible shape-regular partitions.
These are separate from the energy decomposition's reliability assumptions. Local refinement
and degree stay fixed. Neumann face data and macro-local ellipticity bounds follow exact
ancestry. iterations counts solves. A proposed refinement exceeding maximum_cells is not
solved. No contraction or optimality theorem is inferred from the loop, and indicator changes
need not be monotone. local_mesh_factory(macro_mesh, cell_index) can construct
material-fitted local partitions afresh at each level. Static local_meshes are rejected
because their ancestry changes after refinement. macro_refiner defaults to red-green
refinement. Passing refine_longest_edge selects conforming longest-edge propagation; either
operation must provide cell/face ancestors for boundary and trace transfer.
estimator_convention='published' selects the literal numerical terms (5.3)--(5.7); the
default 'energy' uses physical material weights. The printed convention does not imply a
general-SPD energy upper bound. estimator_backend and estimator_workers select
independent execution of local reconstruction and indicator terms separately from the PDE
solver. trace_space declares the scalar face space used initially and
on new macrofaces; its literal degree and continuity override trace_degree
and trace_segments. Existing faces retain their transferred partitions.
solve_step(mesh, skeleton=current_space, **problem_data) may assemble
the user's own mathematical equations at each state. It receives the actual
transferred boundary data and local partitions; its returned physical field
must satisfy the estimator's existing hypotheses. The default retains the
built-in solve. Callback exceptions propagate without changing refinement.
Explicit local/macro refinement balance for the reconstructed Darcy estimator.
BalancedDarcyResult
dataclass
Solved states and the declared refinement decisions, including budget termination.
solve_balanced_adaptive_darcy(mesh: TriangleMesh, *, iterations: int = 5, local_refinement: int = 1, maximum_local_refinement: int = 8, local_error_ratio: float = 1.0, theta: float = 0.5, tolerance: float = 0.0, maximum_cells: int = 10000, trace_degree: int = 0, trace_segments: int = 1, reconstruction_degree: int = 2, estimator_order: int = 8, estimator_convention: Literal['published', 'energy'] = 'energy', ellipticity_lower_bound: Any = None, skeleton: SkeletonSpace | None = None, local_mesh_factory: Callable[[TriangleMesh, int, int], TriangleMesh] | None = None, local_error_indicator: Callable[[DarcySolution], float] | None = None, on_state: Callable[[BalancedDarcyResult], None] | None = None, macro_refiner: Callable[[TriangleMesh, Any], TriangleRefinement] = refine_triangles, solve_step: Callable[..., DarcySolution] | None = None, **problem: Any) -> BalancedDarcyResult
Alternate globally uniform local refinement and marked macro refinement.
Compare norm(eta_div + eta_osc) with local_error_ratio times
sqrt(norm(eta_flux)**2 + norm(eta_nonconf)**2). When the first exceeds
the second, double the local refinement everywhere; otherwise apply
Dörfler marking and conforming red-green macro refinement. These are
refinement proxies, not an exact separation of local and global errors.
No estimator-contraction or complexity-optimality theorem is asserted.
Uniform local refinement keeps shared fine boundary partitions compatible.
A custom local_mesh_factory(mesh, cell, refinement) must likewise give
a globally conforming partition, checked by potential recovery. It can fit
Cartesian material interfaces while leaving the macro mesh unchanged.
A local-resolution limit terminates the loop explicitly rather than
substituting macro refinement for unresolved local indicators.
local_error_indicator(solution) may supply a nonnegative alternative,
such as an energy difference between nested local solves with unchanged
trace data. Such a difference requires a saturation assumption to bound
the actual local error; the policy does not assume that bound.
on_state receives the complete accumulated result after each validated
solve and refinement decision. It may save checkpoints; its exceptions are
propagated. No incomplete solution is passed to the callback.
macro_refiner selects the conforming macro refinement operation and its
exact ancestry. The default is red-green; refine_longest_edge instead
applies longest-edge propagation without green descendants.
estimator_convention selects the literal published numerical terms or
physical-energy material weights, without changing the finite-element PDE.
solve_step(mesh, skeleton=current_space, **problem_data) may assemble
the user's own mathematical equations at each state. It receives the actual
transferred boundary data and local partitions; its returned physical field
must satisfy the estimator's existing hypotheses. The default retains the
built-in solve. Callback exceptions propagate without changing refinement.
Indicator-ranked macro refinement with an explicit mesh-complexity budget.
DarcyBudgetRefinement
dataclass
A conforming refinement, its indicator prefix and the requested cell count.
The actual count can exceed the target because of conformity propagation or the required bulk set. The construction does not claim minimum complexity among all conforming meshes or use an exact/reference solution for marking.
refine_darcy_budget(mesh: TriangleMesh, local_squared: Any, *, target_cells: int, theta: float = 0.5, refiner: Callable[[TriangleMesh, Any], TriangleRefinement] = refine_longest_edge) -> DarcyBudgetRefinement
Enlarge a Dörfler set along decreasing indicators to meet a cell budget.
Start with the deterministic minimal bulk prefix. If its conforming closure already reaches the target, keep it. Otherwise bracket larger prefixes by the full marked set and search between tested prefixes. The returned set contains the bulk prefix and its actual conforming mesh has at least the target count; conformity can cause overshoot. No monotonicity of arbitrary user-supplied refiners or minimal-prefix optimality is assumed. A target beyond the full set's one-call refinement is rejected; use multiple levels. Exact cell/face ancestry is supplied unchanged by the selected refiner.
Estimator-driven conforming tetrahedral MHM adaptation with physical boundary transfer.
AdaptiveDarcy3DResult
dataclass
Solved 3D states, indicators, markings and exact refinement ancestry.
totals: FloatArray
property
Return the selected indicator at each completed state.
solve_adaptive_darcy_3d(mesh: TetraMesh, *, iterations: int = 5, theta: float = 0.5, tolerance: float = 0.0, maximum_cells: int = 10000, trace_degree: int = 0, trace_subdivisions: int = 1, reconstruction_degree: int = 1, estimator_order: int = 7, estimator_convention: Literal['energy', 'published'] = 'energy', ellipticity_lower_bound: Any = None, on_state: Callable[[int, Darcy3DSolution, Darcy3DEstimator], None] | None = None, solve_step: Callable[..., Darcy3DSolution] | None = None, **problem: Any) -> AdaptiveDarcy3DResult
Solve, estimate, mark and bisect tetrahedral edge stars.
The marking policy is original, using the dimension-independent estimator of Barrenechea et al. (2026) and Dörfler bulk sets. It is not a reproduction of a historical 3D mesh sequence and implies no contraction or optimality guarantee. Physical Neumann data and per-cell ellipticity bounds follow their exact parent entities. The callback persists each solved state before refinement.
Estimation requires k>=ell+3, ell<=m<=k, globally conforming fine tetrahedra, represented boundary data, continuous-test equilibrium and certified ellipticity. Theorem 4.7's optimal flux approximation additionally requires polynomial diffusion on each fine tetrahedron, H^(k+1) pressure and A grad(p) in H^(ell+1) intersected with H(div), with compatible shape-regular partitions. Those extra approximation hypotheses are not inferred from material callbacks.
solve_step(mesh, skeleton=current, **problem) may assemble each state
through user-declared public equations. It must return a Darcy3DSolution
in the supplied mesh and trace space; all physical data, coefficient bounds
and refinement ancestry retain their declared meaning. None preserves
the built-in Darcy solver. Selecting a callable changes only state assembly,
not the estimator, marking, boundary transfer or stopping policy.
Face-jump refinement indicators for the one-level Darcy MHM analysis.
DarcyJumpEstimator
dataclass
Face indicators with the original cell-side multiplicity.
The face indicators follow Araya et al. (2013).
Interior faces contribute once to each of their two incident cells. The published reliability theorem concerns exact local lifts; this quantity alone does not bound the error of finite-dimensional local solves.
total: float
property
Return the square root of the sum over all macrocell sides.
estimate_darcy_jumps(solution: DarcySolution, *, dirichlet: Any = 0.0, neumann: dict[int, Any] | None = None, ellipticity_lower_bound: Any = None, calibration: float = 1.0, order: int | None = None) -> DarcyJumpEstimator
Evaluate Araya et al. (2013), equations (5.1)--(5.3).
Use R_F=-jump(p)/2 on interior macrofaces, R_F=g_D-p on Dirichlet faces, and zero on Neumann faces. Then eta_F=c_lc_min||R_F||/sqrt(H_F), where H_F is the original macroface length and c_min is the square root of a certified lower eigenvalue bound for permeability. Integration splits at both fine-edge partitions and all skeletal breakpoints.
calibration is c_l, not a proven constant-one reliability factor.
The paper's Section 6 uses 3, 7, 18 and 50 for uniform trace degrees 0--3.
Its theorem assumes exact local lifts, regularity and represented boundary
data. Finite local errors require an additional estimator, such as the
weighted flux-reconstruction estimator; no bound is asserted here.
Nested local energy differences at a fixed physical MHM skeleton trace.
LocalDarcyRefinementEstimate
dataclass
Energy differences between nested local solves with the same macro flux.
This is a computed refinement difference, not an error bound without a separately justified saturation estimate. Local means do not enter its norm.
total: float
property
Return the square root of the sum of all local energy differences.
estimate_darcy_local_refinement(solution: DarcySolution, *, quadrature_order: int | None = None, local_solver: str = 'scipy', refinement_precision: Literal['double', 'extended'] = 'double', backend: Literal['serial', 'thread', 'process'] = 'serial', workers: int | None = None) -> LocalDarcyRefinementEstimate
Red-refine every local triangle and solve with unchanged physical face data.
The finite-element degree, source, permeability and skeletal coefficients
remain fixed. The enriched local pressure has zero mean; the energy norm
only compares gradients. Return the difference in the genuine broken
K energy norm using material-cut quadrature and exact parent-cell
ancestry. Point sources and mixed pressure spaces are outside this contract.
No reconstructed-divergence term is used as a surrogate local-error norm.
backend distributes independent local solves in cell order. Process
workers use spawn and require picklable source/material callbacks; all
native factors are constructed and released inside their owning worker.
Residual-indicator mesh sizes and optional FreeFEM/BAMG remeshing.
The nodal metric follows the scalar residual-indicator construction documented by FreeFEM. It is independent of the PDE solver and does not prescribe a bulk marking rule or preserve nested meshes.
ResidualMeshSize
dataclass
Current and requested nodal sizes and the lumped indicator projection.
residual_mesh_size(mesh: TriangleMesh, local_squared: Any, *, coefficient: float = 1.0) -> ResidualMeshSize
Compute the documented isotropic residual metric from squared indicators.
The current size at a vertex is the average length of its incident edges,
counted once per incident triangle. A continuous P1 indicator is obtained
by area-weighted lumped projection of the cellwise square roots. Divide the
current size by clip(projected / (coefficient * mean(cellwise)), 1/3, 3).
Thus both refinement and coarsening are allowed. A zero indicator leaves
sizes unchanged. Unused vertices and invalid data are rejected explicitly.
remesh_freefem(mesh: TriangleMesh, sizes: Any, *, executable: str = 'FreeFem++', max_vertices: int = 10000, timeout: float = 120.0) -> TriangleMesh
Remesh with BAMG using a supplied continuous P1 physical size field.
A separately installed FreeFEM executable is required. Only mesh generation
is delegated: the call uses adaptmesh(IsMetric=1, splitpbedge=1) and does
not solve a PDE. The requested size is interpolated in the input P1 space;
the output need not be nested. Material interfaces and boundary data must be
transferred by the caller's physical functions, not by cell-index ancestry.
The unchanged domain area is checked after reading the returned mesh.
Face residuals of the one-level primal elasticity estimator.
The estimator follows Harder, Madureira and Valentin (2016).
The reliability theorem assumes exact local Neumann lifts. With computed local finite elements this is a face indicator, not a certified estimator of the full two-level error. In particular, an unresolved continuous local field can have a zero jump indicator and nonzero interior error.
PrimalElasticityIndicator
dataclass
Per-segment displacement jumps scaled by the declared ellipticity constant.
face_squared contains c_min²/H_F times the squared displacement residual.
Internal faces have the factor one half in the displacement jump and count
twice in eta. The denominator is the original macroface length. The
object records no assertion that finite local lifts satisfy the one-level
reliability theorem; that additional hypothesis is not numerically inferred.
eta: float
property
Return the face indicator with the macrocell-boundary multiplicity.
The indicator follows Harder, Madureira and Valentin (2016).
estimate_primal_elasticity_error(solution: PrimalElasticitySolution, *, dirichlet: Any = (0.0, 0.0), c_min: float, full_dirichlet: bool, quadrature_order: int = 8) -> PrimalElasticityIndicator
Evaluate the face estimator under an explicit full-Dirichlet contract.
Use equations (5.4)--(5.6) of Harder, Madureira and Valentin (2016).
Supply c_min such that A(x) epsilon:epsilon >= c_min² |epsilon|² everywhere. The supplied bound is checked at face integration points, but only the user can establish its global validity for a generic callable. The theorem's constants depend on the material; no incompressibility-uniform or unit reliability constant is claimed. For finite local approximations, refine the local space separately to assess the unmeasured local consistency error.
References
-
Rodolfo Araya, Christopher Harder, Diego Paredes, and Frédéric Valentin (2013). Multiscale Hybrid-Mixed Method, SIAM Journal on Numerical Analysis 51(6), 3505–3531. DOI: 10.1137/120888223.
-
Gabriel R. Barrenechea, Larissa Martins, Weslley Pereira, and Frédéric Valentin (2026). An H(div; Ω)-Conforming Flux Reconstruction for the Multiscale Hybrid-Mixed Method, Multiscale Modeling & Simulation 24(2), 399–428. DOI: 10.1137/24M1673073.
-
Christopher Harder, Alexandre L. Madureira, and Frédéric Valentin (2016). A hybrid-mixed method for elasticity, ESAIM: M2AN 50, 311–336. DOI: 10.1051/m2an/2015046.
Source modules
Conforming local closure for adaptively segmented flow skeletons.
refine_flow_local_meshes(skeleton: SkeletonSpace, local_meshes: tuple[TriangleMesh, ...], marked_local: np.ndarray, maximum_cells: int, *, fine_marked: tuple[np.ndarray, ...] | None = None) -> tuple[TriangleMesh, ...] | None
Resolve every new trace vertex while refining only the required local boundary cells.
Local-error dominance red-refines all triangles of that local mesh once,
unless fine_marked supplies selected cells for longest-edge refinement.
Subsequent longest-edge propagation contains every skeletal breakpoint,
preserving conformity and avoiding uniform closure of unrelated interiors.
Return None before a local cell cap is exceeded. The input meshes are
never mutated, so the caller can retain its last solved configuration.