Skip to content

experimental/adrh: time integration against the Dirac-IC solutions; a shared regression module - #101

Open
cekees wants to merge 1 commit into
adrh-spacesfrom
adrh-time
Open

cekees wants to merge 1 commit into
adrh-spacesfrom
adrh-time

Conversation

@cekees

@cekees cekees commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Stacked on #100 (base adrh-spaces). Nothing outside experimental/adrh/ changes.

The engine's backward-Euler mass term was already in the kernel; this adds what is needed to run and check it: TransientLinearADR<nS> (Proteus's LinearVADR_ConstantCoefficients), DiracExact<nS> (the Gaussian of AnalyticalSolutions.LinearADR_Decay_DiracIC, verified in #90) and ExactBC<nS, Exact>, a boundary functor fed by any exact solution's value and gradient at (x, t) in all seven boundary kinds; bindings transient_adr / transient_adr_faces; the driver ad_dirac_regression.py with the Proteus model ad_dirac_p.py / ad_dirac_n.py (BackwardEuler + FixedStep, DT = T/NSTEPS); and regression_common.py, which the steady and transient drivers now share.

Engine vs Proteus nodal solutions at T = 0.3, all seven boundary modes: P1, P2, Q1 in 2D (nn up to 65) to <= 9e-14; P1, P2, Q1 samples in 3D to <= 1.1e-13. Against the exact solution the error converges at rate 0.9-1.0 in dt and saturates at the time error in h, as backward Euler should.

One Proteus behaviour had to be mimicked for that agreement (AD_FIRST_STEP=proteus, off by default), and it looks like a bug worth a decision: in NumericalSolution.calculateSolution, after stepController.initialize_dt_model(t0, t1) has moved the time to t1, each level model's getResidual runs once more (setting the strongly imposed Dirichlet dofs to g(t1)), and only then does stepController.initializeTimeHistory() copy that evaluation's mass into BackwardEuler.m_last. In the first step the eliminated boundary dofs therefore have no time derivative; from the second step on m_last comes from the previous step's final evaluation and is right. The effect is O(dt^2), local to the boundary-adjacent rows (1.4e-4 after one step of 0.3 at nn = 17; 5e-8 at dt = 0.3/16, nn = 65), and absent in the weak-Dirichlet classes, which eliminate nothing. Not changed here: a fix moves every transient result with strong Dirichlet data by that amount, goldens included.

Also recorded in the regression: a _p file must not use x0 or L for anything but the domain (Proteus reads p.x0, p.L for its regular mesh); run_proteus now asserts the mesh spans the unit domain. Design note ADRH_ENGINE_DESIGN.md section 18 (beside the campaign notes, not in this repo).

🤖 Generated with Claude Code

…ared regression module

TransientLinearADR<nS> (proteus's LinearVADR_ConstantCoefficients: m = u, f = b u, a = kappa I,
r = c u), DiracExact<nS> (the Gaussian of AnalyticalSolutions.LinearADR_Decay_DiracIC with
proteus's exponent cut-off) and ExactBC<nS, Exact>, the boundary functor fed by an exact
solution's value and gradient at (x, t) in all seven kinds; bindings transient_adr and
transient_adr_faces. regression_common.py holds what the steady and the new transient driver
share; ad_regression.py uses it. ad_dirac_regression.py steps the engine (backward Euler,
one linear solve per step, boundary data at the new time) against the proteus model
ad_dirac_p.py / ad_dirac_n.py (BackwardEuler + FixedStep, DT = T / NSTEPS) and the exact
solution, with h and dt sweeps.

Engine vs proteus at T = 0.3, all seven boundary modes: P1, P2, Q1 in 2D <= 9e-14, 3D samples
<= 1.1e-13, once the engine mimics (AD_FIRST_STEP=proteus) how proteus builds the first
step's old mass: NumericalSolution.calculateSolution evaluates the residual once more after
initialize_dt_model has moved the time to t1, with the strongly imposed Dirichlet dofs set to
g(t1), and then stepController.initializeTimeHistory copies that mass into m_last, so the
boundary dofs have no time derivative in step one (O(dt^2), boundary-adjacent rows only; later
steps are right; the weak-Dirichlet classes are unaffected). The engine's default keeps the
true old state. Error against the exact solution: rate 0.9-1.0 in dt, saturating in h.

Trap: a _p file must not name anything but the domain x0 or L (proteus reads p.x0 and p.L for
its regular mesh); run_proteus asserts the unit domain.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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