Internals

Non-exported machinery, documented for contributors. Nothing on this page is part of the public API contract.

Geometry

The single implementation of face/element geometry (normals, Jacobians, quadrature-point maps) consumed by DGContext and the batched HDG assembly.

TwoDG.Geometry.GeometricFactors — Type
GeometricFactors(master, mesh; T=Float64)

One-time precomputation of all mesh geometry at quadrature points, shared by every discretization (the DG residual kernels consume it directly as DGContext; the HDG/CG caches compose it): face/element connectivity resolved to plain index arrays (no runtime findfirst), face and element geometry evaluated at quadrature points, and explicit inverse mass matrices. All fields are plain arrays of eltype T (Int32 for indices), so the whole cache moves to a GPU with Adapt.adapt(CuArray, gf).

Faces 1:ni are interior, ni+1:nf are boundary (the ordering of mesh.f).

Field shapes (npl volume nodes, npf face nodes, ng/ngf quadrature points):

  • facecon (npf, 2, nf): volume-node indices of face nodes; side 1 = left element, side 2 = right (unused for boundary faces).
  • f_el (nf, 2): (left element, right element); column 2 is -ib (negative boundary tag) for boundary faces, as in mesh.f.
  • nlg (ngf, Dim, nf), dws (ngf, nf), pfg (ngf, Dim, nf): outward unit normal (w.r.t. left element), weighted measure, and physical coordinates at face quadrature points.
  • vol ::VolumeTables: volume geometry in the compact affine/curved split layout (dense per-element tables only for curved elements).
  • Minv (npl, npl, nt): inverse element mass matrices (dense for every element — same cost class as the solution itself).
  • shapf (npf, ngf), shap (npl, ng): shape-function values (shared).

The pre-split dense tables remain available as materializing properties: gf.shapd (npl, ng, Dim, nt), gf.wjac (ng, nt), gf.pg (ng, Dim, nt) allocate and fill the full array on access — convenient for diagnostics (sum(ctx.wjac)) and one-time CPU consumers, wasteful inside loops.

Deprecated property aliases (one release, 2D): shapx/shapy materialize shapd[:, :, 1/2, :]; sh1d, np1d, ng1d read shapf, npf, ngf.

source
TwoDG.Geometry.RefTables — Type
RefTables(master)

Reference-element tabulations shared by all geometry evaluations: shape values/derivatives at volume and face quadrature points, quadrature weights, weighted derivative tables and the reference mass matrix. Dimension-generic: the face tables come from the master element's recursive face element.

source
TwoDG.Geometry.SideGeometry — Type
SideGeometry(master, mesh; T=Float64)

Face geometry in element-local orientation, one entry per (local side, element) — what the HDG trace assembly needs (its trace basis follows the element's own canonical traversal perm[:, s, 1], not the global face's stored direction):

  • nl (ngf, Dim, Dim+1, nt): outward unit normal of side s of element e,
  • sw (ngf, Dim+1, nt): quadrature-weighted side measure,
  • pfs (ngf, Dim, Dim+1, nt): physical coordinates of the side quadrature points.

Evaluated isoparametrically from the side's high-order nodes, so curved faces are exact to the geometric order.

source
TwoDG.Geometry.VolumeTables — Type
VolumeTables

Volume geometry at quadrature points in the memory-compact split layout: dense per-element tables are stored only for curved elements; straight (affine) elements — the bulk of any real mesh — store just their constant Jacobian data and share one set of reference tables. This is what keeps 3D meshes affordable: the dense shapd table alone is O(npl·ng·Dim) ≈ 165 KB per element at p = 3 on tets, while the affine representation is O(Dim²).

  • curved_ix (nt,): 0 for affine elements, else the element's column in the dense tables below.
  • cshapd (npl, ng, Dim, ntc), cwjac (ng, ntc), cpg (ng, Dim, ntc): dense quadrature-/Jacobian-weighted derivative tables, weighted Jacobians, and quadrature-point coordinates of the ntc curved elements.
  • aC, aJ (Dim, Dim, nt), av0 (Dim, nt), adetJ (nt,): per-element adjugate, Jacobian, map origin (first vertex), and det J of the affine map x(ξ) = av0 + aJ ξ (zero for curved elements).
  • rshapdg (npl, ng, Dim), rgpts (ng, Dim), rgwgh (ng,): shared reference tables (quadrature-weighted reference derivatives, quadrature points and weights).

Kernels branch per element on curved_ix (see the accessors quad_coords, quad_weight and the flux-rotation pattern in the DG kernels); the trade of a few extra FLOPs for O(npl·ng) less memory traffic per affine element is a win on both backends.

source
TwoDG.Geometry.adjugate — Method
adjugate(J) -> SMatrix

Adjugate (transposed cofactor matrix) of a Dim × Dim Jacobian: adjugate(J) == det(J) * inv(J) without the division — the exact-arithmetic form the affine geometry path contracts reference derivatives with.

source
TwoDG.Geometry.affine_jacobian — Method
affine_jacobian(verts, ::Val{Dim}) -> (J, detJ, C)

Constant Jacobian data of the affine map from the reference simplex to the element with vertex coordinates verts (Dim+1, Dim): J[d, k] = ∂x_d/∂ξ_k, its determinant, and its adjugate C = detJ * inv(J). The one definition shared by the curved-element evaluator and the compact affine storage, so both produce bit-identical values.

source
TwoDG.Geometry.element_geometry! — Method
element_geometry!(shapd, wjac, pg, rt, coords; verts=nothing) -> M

Fill, for one element with high-order node coordinates coords (npl, Dim):

  • shapd (npl, ng, Dim): quadrature- and Jacobian-weighted physical derivative tables (∫ ∂φ/∂x_d ⋅ f becomes shapd[:, :, d] * f(quad)),
  • wjac (ng,): gwgh .* detJ,
  • pg (ng, Dim): physical coordinates of the volume quadrature points,

and return the element mass matrix M (npl, npl).

For a straight (affine) element pass verts, the (Dim+1, Dim) vertex coordinate matrix; the Jacobian is then constant and M is the scaled reference mass. Otherwise the isoparametric map is evaluated per quadrature point (curved element).

source
TwoDG.Geometry.face_geometry! — Method
face_geometry!(nlg, dws, pfg, rt, coords; tangent=nothing)

Fill, for one face with high-order node coordinates coords (npf, Dim) (ordered left-element-outward, i.e. dgnodes[perml, :, el]):

  • nlg (ngf, Dim): outward unit normal w.r.t. the left element,
  • dws (ngf,): quadrature-weighted face measure,
  • pfg (ngf, Dim): physical coordinates of the face quadrature points.

For a straight face pass tangent, an NTuple{Dim-1, SVector{Dim}} of vertex-to-vertex edge vectors; the metric is then constant. Otherwise the metric is evaluated from the high-order nodes (curved face).

source
TwoDG.Geometry.face_normal — Method
face_normal(τ) -> SVector{2}
face_normal(τ₁, τ₂) -> SVector{3}

Outward (unnormalized) face normal from the face tangent vector(s): the quarter-turn rotation of the single tangent in 2D, the cross product of the two tangents in 3D. Its norm is the face measure Jacobian. This is the one place where 2D and 3D geometry genuinely differ.

source
TwoDG.Geometry.inscribed_diameter — Method
inscribed_diameter(p, t, it, ::Val{Dim}) -> h

Inscribed-circle (2D) / inscribed-sphere (3D) diameter 2r = 2·Dim·|K|/|∂K| of the vertex simplex of element it — the h that controls explicit CFL limits. p/t are the mesh vertex coordinates and element connectivity.

source
TwoDG.Geometry.quad_coords — Method
quad_coords(vol, g, e, ::Val{Dim}) -> SVector{Dim}

Physical coordinates of volume quadrature point g of element e: av0 + aJ ξ_g for affine elements, a dense-table read for curved ones. Device-inlineable.

source
TwoDG.Geometry.quad_weight — Method
quad_weight(vol, g, e) -> T

Quadrature-weighted Jacobian gwgh[g] * detJ at volume quadrature point g of element e. Device-inlineable.

source

Private helpers

TwoDG.Interface._dg_physics — Method
_dg_physics(prob::DGProblem) -> DGPhysics

Assemble the physics bundle the DG kernels consume: the equation, the per-boundary condition tuple (validated against the mesh's boundary count), the numerical flux, source, and stabilization — problem-level nothings resolved to the equation's defaults.

source
TwoDG.Interface._ordered_bcs — Method
_ordered_bcs(bc, mesh)

Boundary conditions may be given positionally (a vector/tuple indexed by boundary tag) or, when the mesh generator attached boundary names, as a NamedTuple keyed by those names, e.g. for mkmesh_square: (bottom=Dirichlet(), right=Neumann(), top=Dirichlet(), left=Neumann()).

source
Base.ndims — Method
ndims(mesh) -> Int

Spatial dimension of the mesh (the Dim type parameter): 2 for triangles, 3 for tetrahedra.

source
TwoDG.Meshes.mkelcon — Method
mkelcon(t2f, t2o, porder, face_plocal) -> elcon (nps, 4, nt)

3D (tetrahedral) trace connectivity: face f owns the nps = (porder+1)(porder+2)/2 trace nodes (f-1)nps+1 : f*nps in the face's canonical node order (face_plocal, the triangle face element's nodes), and each element couples through orient_perm at its orientation code.

source
TwoDG.Meshes.mkelcon — Method
mkelcon(t2f, t2o, porder)

Element-to-global trace-node connectivity elcon (porder+1, 3, nt) from the element-to-face map t2f and orientation codes t2o: face f owns trace nodes (f-1)*(porder+1)+1 : f*(porder+1) in the face's canonical order, and each element traverses them through orient_perm[:, t2o[it, s]].

source
TwoDG.Meshes.orient_perm — Method
orient_perm(nps, Val(2))          -> (nps, 2)  permutation table
orient_perm(face_plocal, Val(3))  -> (nps, 6)  permutation table

Trace-node permutations of one face under each orientation code: an element seeing a face with orientation o couples its k-th local trace node (its own canonical face traversal) to the face's global trace node op[k, o]. In 2D this is the identity and the reversal; in 3D it is built constructively from the triangle face element's node barycentrics: op[k, o] is the canonical face node m whose coordinates permuted by the o-th triangle symmetry match node k (face_plocal[m, σ_o] == face_plocal[k, :]).

source
TwoDG.Meshes.simpvol — Method
simpvol(p::Matrix{T}, t::Matrix{Int}) where T<:Real

Compute signed volumes of the simplices of a mesh: areas of triangles (t (nt, 3)) or volumes of tetrahedra (t (nt, 4)), positive for counterclockwise / right-handed vertex ordering.

Parameters:

  • p: N×Dim matrix of vertex coordinates
  • t: M×(Dim+1) matrix of simplex vertex indices

Returns:

  • Vector of signed volumes for each simplex
source
TwoDG.Meshes.straight_dgnodes — Method
straight_dgnodes(p, t, plocal) -> dgnodes (npl, 2, nt)

Map the master-element nodes plocal (barycentric, (npl, 3)) affinely into every triangle of (p, t). This is the straight-element part of createnodes; use it when the high-order nodes will be transformed analytically afterwards (e.g. mkmesh_trefftz's conformal maps).

source
TwoDG.Meshes.unique_with_inverse — Method
unique_with_inverse(arr)

Find unique rows in a matrix and return inverse mapping. Similar to np.unique(arr, return_inverse=True, axis=0) in NumPy.

source
TwoDG.Masters.build_face_perm — Method
build_face_perm(::Val{Dim}, plocal, face_plocal) -> perm (npf, Dim+1, norient)

Constructive face-node permutation table: for each local face j and orientation o, perm[:, j, o] lists the volume-node indices whose barycentric coordinates (restricted to the face's vertices) match the face element's nodes face_plocal under the o-th symmetry of the face simplex. No hand-tabulated index magic — works for every porder, provided the volume node set restricted to each face equals the face element's node set (true for the symmetrized localpnts/localpnts3d distributions; asserted).

source
TwoDG.Masters.gaussjacobi — Function
gaussjacobi(n, α, β=0) -> (x, w)

Gauss–Jacobi points and weights on [-1, 1] with weight (1-x)^α (1+x)^β: n points integrate f(x) (1-x)^α (1+x)^β exactly for polynomial f of degree ≤ 2n-1. Same Vandermonde-solve construction as gaussquad1d (which is the α = β = 0 case); by orthogonality only the constant polynomial has a nonzero weighted moment, 2^(α+β+1) B(α+1, β+1).

source
TwoDG.HybridizableDiscontinuousGalerkin.apply_blockjacobi — Method
apply_blockjacobi(B, v)

Applies a block Jacobi preconditioner to a vector.

Arguments

  • B::AbstractArray: Block Jacobi preconditioner with dimensions (ncf, ncf, nf)
  • v::AbstractArray: Vector to be preconditioned (can be 1D flattened or 2D array)

Returns

  • w::Array: Preconditioned vector in the same format as input v
source
TwoDG.HybridizableDiscontinuousGalerkin.boundary_face_quad — Method
boundary_face_quad(mesh, master, i)

Quadrature data of boundary face i in global face-node order: the physical quadrature points Xq (ngf × Dim), the weighted measure wds, and the face mass matrix Tm. Used to impose boundary data through its L2 projection P∂g onto Pk(F) — nodal interpolation of the data would introduce an O(h^{k+1}) perturbation that destroys the superconvergence of the method.

source
TwoDG.HybridizableDiscontinuousGalerkin.global_assembly — Method

Assembles the global matrix and vector for a specific face.

Parameters:

AE : Array Element matrices FE : Array Element vectors f : Array Face to element connectivity t2f : Array Element to face connectivity G : Array (npf, nfe, ne) Element-local -> face-canonical trace-node gather (see hdg_densesystem) ncf : Int Number of components per face nbf : Int Number of neighboring faces nfe : Int Number of faces per element i : Int Face index

Returns:

Ai : Array Global matrix for face i Fi : Array Global vector for face i

source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_applydbc! — Method
hdg_applydbc!(ae, fe, master, mesh, dbc)

Applies the strong Dirichlet boundary condition dbc to the boundary-face rows of the element matrices/vectors ae, fe in place. The identity rows are set in the element's own face-node order, so the assembled system pins each global trace node to the boundary value at its own coordinate for any face orientation (2D or 3D).

source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_densesystem — Method
hdg_densesystem(AE, FE, f, t2f, elcon)
hdg_densesystem(AE, FE, f, t2f, t2o, npf)

Assembles the global system in dense face-block format.

Arguments

  • AE: Element matrices
  • FE: Element vectors
  • f: Face to element connectivity
  • t2f: Element to face connectivity
  • elcon (npf, nfe, ne): orientation-resolved element-to-global trace-node connectivity (mkelcon); the second form reconstructs the 2D (two-orientation) elcon from t2o and the trace size npf for backwards compatibility.

Returns

  • A: Global matrix in dense format
  • F: Global vector in dense format
source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_elem_face — Method
hdg_elem_face(dg, master, s)

Face metric terms of local face s of the element with nodes dg: the volume node indices ps on the face (canonical traversal), the outward unit normal n (ngf × Dim), the face measure Jacobian ds, and the weighted measure wds at the face quadrature points. The normal comes from face_normal dispatch (tangent rotation in 2D, cross product in 3D).

source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_elem_volume — Method
hdg_elem_volume(dg, master)

Volume metric terms of one (possibly curved) element with nodes dg (npl × Dim): shape values shap (npl × ng), the quadrature- and Jacobian-weighted physical derivative tables shapd (npl × ng × Dim, shapd[m, g, d] = w_g jac_g ∂φ_m/∂x_d), the mass matrix M, the convection-type matrices C (npl × npl × Dim, C[m, n, d] = (φ_m, ∂_d φ_n)), and the weighted Jacobian wjac.

source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_elemmats — Method
hdg_elemmats(master, mesh, source, dbc, param)

Computes the HDG element matrices/vectors ae (3nps, 3nps, nt), fe (3nps, nt) for all elements (threaded) and applies the strong Dirichlet boundary conditions dbc to the boundary-face rows.

source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_matvec — Method
hdg_matvec(A, F, f2f)

Performs matrix-vector multiplication for HDG method using face-to-face connectivity.

Arguments

  • A: Global matrix in dense format (ncf, ncf, nbf, nf)
  • F: Vector to be multiplied (flattened)
  • f2f: Face-to-face connectivity

Returns

  • v: Result of matrix-vector multiplication (flattened)
source
TwoDG.HybridizableDiscontinuousGalerkin.hdg_ns_elemmat — Method
hdg_ns_elemmat(dg, master, ν, τ, um, λe, fe, ffun, uolde, dtinv)

Element matrices for one Newton step of the HDG incompressible Navier-Stokes discretization, linearized about the velocity um (npl × Dim) and the trace components λe (nfc × Dim). The body force is either the DG field fe (npl × Dim, integrated exactly) or the function ffun (evaluated at the quadrature points — interpolating it at the nodes instead would spoil the superconvergence of the method); uolde is the velocity at the previous time level for backward Euler (or nothing), and dtinv = 1/Δt (0 for steady state).

The velocity gradient is eliminated analytically and the local (u, p) system is statically condensed. Returns (Ke, Ge, re, crow, Z, area): the condensed trace matrix (Dim·nfc × Dim·nfc, trace ordered [λ₁; …; λDim]), the mean-pressure coupling column, the condensed right-hand side, the element- compatibility row ⟨λ·n, 1⟩∂K, the local solution operator Z = A⁻¹[B bρ r] for the recovery of (u, p), and the element measure.

source
TwoDG.Callbacks.finish! — Method
finish!(cb, state) -> nothing

Lifecycle hook called once by the solve loop after the last step (regular end or callback-requested stop). No-op by default; built-ins use it for a final analysis row / snapshot / timing summary.

source
TwoDG.Callbacks.initialize! — Method
initialize!(cb, state) -> nothing

Lifecycle hook called once by the solve loop before the first step, with state.t == t0. The default is a no-op, so plain closures need nothing; built-in callbacks use it to anchor time-based schedules, record the t0 conservation reference, print headers, etc. Extend it for custom callback types (TwoDG.Callbacks.initialize!(cb::MyCallback, state) = ...).

source