Skip to content

experimental/adrh: Lagrange spaces (P1, P2 simplices; Q1 cubes) with isoparametric geometry, regression over every space - #100

Merged
cekees merged 2 commits into
mainfrom
adrh-spaces
Oct 25, 2026
Merged

cekees merged 2 commits into
mainfrom
adrh-spaces

Conversation

@cekees

@cekees cekees commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Follows #96 (merged). Nothing outside experimental/adrh/ changes. This is the "all finite-element spaces" item of the engine decisions, for the Lagrange spaces Proteus's regular meshes offer.

  • spaces.h: a Space gives basis values and reference gradients at a reference point, the map from a face's own coordinates to the element's, each face's outward reference normal (scaled so n dS = |det J| J^{-T} n_ref d(eta) against the face quadrature weights) and each face's vertices. PointGeometry builds the isoparametric map from the same basis at every quadrature point: Jacobian, inverse, |det|, physical gradients, the point, the face normal and measure. P1Simplex, P2Simplex (Proteus's edge order) and Q1Cube (Proteus's reference cube [-1,1]^d and node order), each in 2 and 3 dimensions.
  • engine.h: elementKernelSpace<Coefficients, Space>, the masked weak form of masked.h with basis and Jacobian per point. boundary.h: exteriorFaceKernel takes the Space as a third template parameter (default P1), so segments, triangles, quadrilateral edges and hexahedral faces share one code path; the penalty's h is the longest distance between the face's vertices, which for hexahedra includes the diagonals as Proteus's computeGeometricInfo_hexahedron does.
  • The module dispatches on (nd, space) and exposes space_info, basis_values and quadrature_geometry for the driver's face selection and norms. ad_regression.py takes AD_SPACE=p1|p2|q1; element DOF coordinates come from the dof map's Lagrange nodes, Dirichlet rows are every Lagrange node on the plane, boundary faces are picked by the space's face-vertex lists.

Engine vs Proteus nodal solutions, all seven boundary modes (strong, mixed, inflow, robin, iipg, nipg, sipg) per space and dimension, 42 runs:

space 2D 3D L2 rate
P1 (triangles / tets) <= 4.6e-14, nn 9-65 <= 7.1e-15, nn 5-17 2.0
P2 (triangles / tets) <= 2.0e-14, nn 5-33 <= 1.2e-14, nn 3-9 3.0
Q1 (quads / hexes) <= 1.1e-14, nn 9-65 <= 1.2e-14, nn 5-17 2.0

P1 through the generic kernel reproduces the previous kernel's numbers exactly. Design note ADRH_ENGINE_DESIGN.md section 17 (beside the campaign notes, not in this repo).

🤖 Generated with Claude Code

cekees and others added 2 commits October 24, 2026 06:04
… geometry; P2 regression against Proteus

spaces.h: a Space gives basis values and reference gradients at a reference point, the map
from a face's own coordinates to the element's, the outward reference normal scaled so that
n dS = |det J| J^{-T} n_ref d(eta) against the face quadrature weights, and the vertices of
each face. PointGeometry builds the isoparametric map from the same basis at every quadrature
point: Jacobian, |det|, inverse, physical gradients, the point, and the face normal and measure.
P1Simplex and P2Simplex (proteus's edge order: 2D (0,1),(1,2),(0,2); 3D (0,1),(1,2),(2,3),
(0,2),(1,3),(0,3)) in 2 and 3 dimensions.

engine.h: elementKernelSpace<Coefficients, Space>, the masked weak form with the basis and
Jacobian taken per quadrature point. boundary.h: exteriorFaceKernel takes the Space as a third
template parameter (default P1) and gets its geometry from the same map, so segments, triangles
and later quadrilaterals share one code path. The module dispatches on (nd, space) and exposes
basis_values for the driver's norms.

ad_regression.py: AD_SPACE=p1|p2; element dof coordinates come from the dof map's Lagrange
nodes, faces are picked by their vertices, Dirichlet rows by every Lagrange node on the plane.
Engine vs Proteus nodal solutions, 7 boundary modes x 2 spaces x 2 dimensions (28 runs): all
<= 2.0e-14 (P2) and <= 4.6e-14 (P1, unchanged through the generic kernel). P2 L2 rates
3.00 (2D) and 2.9-3.0 (3D).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er every space

spaces.h: Q1Cube<nS> on proteus's reference cube [-1, 1]^nS with ReferenceCube.nodeList's node
order; faces k = 2 I + side with the other reference coordinates as the face's own, on
[-1, 1]^(nS-1) like CubeGaussQuadrature(nS - 1). The trilinear map goes through the same
PointGeometry as the simplices. The face diameter for the penalty, the longest distance
between the face's vertices, includes the hexahedral face's diagonals, as proteus's
computeGeometricInfo_hexahedron does.

The module dispatches "q1" and exposes space_info (dofs, vertices, each face's vertices) and
quadrature_geometry (points and |det J|) so the driver picks boundary faces by the space's
face-vertex lists and computes norms with the engine's geometry for any space. ad_sine_n.py
selects the Q1 space, CubeGaussQuadrature and a regular quadrilateral / hexahedral mesh for
AD_SPACE=q1.

Engine vs proteus nodal solutions, 7 boundary modes: Q1 2D (nn 9-65) <= 1.1e-14, 3D (nn 5-17)
<= 1.2e-14, L2 rates 2.0; the P1 and P2 numbers are unchanged. 42 runs in all over the three
spaces and two dimensions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@cekees
cekees merged commit 9bc70a0 into main Oct 25, 2026
10 checks passed
@cekees
cekees deleted the adrh-spaces branch October 25, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant