Residual layer
The pure-residual surface underneath solve: a ProblemDef is an immutable problem description, a Workspace holds the mutable buffers, and residual! evaluates the semi-discrete right-hand side. This is the layer to target for custom solver loops; see the User Guide's solver-configuration chapter.
Problem definition and workspaces
Pyrolysis.Problem.SimulationMode — Type
SimulationMode{Mesh, Rad, Trans, Chi}Bundled trait carrying all four orthogonal axes as type parameters. The residual takes SimulationMode as a single parameter so adding new axes does not multiply method-table size.
Pyrolysis.Problem.StateLayout — Type
StateLayout — unified state-vector descriptor.
Describes the packing of the state vector u in terms of the bundled SimulationMode. Block layout (T, then ξ, then optional z, then optional χ) is the single source of truth for every pack / unpack / view operation in the package.
Block presence is strictly determined by SimulationMode:
- T and ξ blocks are always present.
- z block is present iff
mesh_mode(mode) isa ALE. - χ block is present iff
chi_mode(mode) isa WithChi.
Type Parameters
NC: number of componentsMode <: SimulationMode: bundled trait
Block-major offsets (1-based, with n = n_cells)
- T:
1 .. n - ξ:
n + (j-1)*n + (1..n)forj ∈ 1:NC - z:
n*(1+NC) + (1..n_nodes)(ALE only) - χ: trailing block of length
n(only ifWithChi)
Pyrolysis.Problem.ProblemDef — Type
ProblemDef — immutable problem definition.
A ProblemDef carries the unchanging contract of a pyrolysis problem: material, topology, boundary conditions, initial conditions, time span, options. Every mutable cache lives in Workspace instead.
This split is what makes the residual a pure function of (du, u, def, t, ws) and unlocks parameter sweeps, sensitivity analysis, and pluggable Jacobian backends in later phases.
Field access is direct: read def.material, def.topology, def.bcs, def.tspan, def.mode, def.options. The single helper is get_option for fallback semantics on the runtime-options NamedTuple.
Type Parameters
M: Material typeTop: MeshTopology typeB: BoundaryConditionSet typeIC: Initial-conditions container typeMode: bundled SimulationMode typeOpts: NamedTuple of runtime options
Pyrolysis.Problem.Workspace — Type
Workspace — mutable container that owns every per-step cache and work array used by the unified residual.
The Workspace is the only place where mutable per-step state lives. Material, boundary conditions, and the radiation model are read from def::ProblemDef (the immutable side); the workspace does not duplicate them.
Every cache is parameterized so that Nothing can appear as a concrete type parameter rather than as a Union{Nothing, T}. Trait dispatch on the type — not runtime isnothing checks — decides whether to touch a cache.
Type Parameters
NC: number of componentsMode: bundledSimulationMode(matches the parentProblemDef)Tu: scalar eltype carried by the live state (Float64for the canonical workspace;ForwardDiff.Dual{Tag, Float64, N}for the AD workspace allocated by an AD Jacobian backend)MeshT: Reference to the mutableMesh1Dcarrying connectivity and per-step geometry. The mesh is read-only during residual / Jacobian assembly; writes happen only between integrator-accepted steps viaStepUpdateCallback. Kernels only read connectivity (cell IDs, face IDs, neighbor lookups, boundary tags) off the mesh; live state and state-derived geometry live instate_cache.GeomT:MeshGeometryviewLayoutT:StateLayoutdescriptorGeomCacheT:Geometry.GeometryCachefor ALE;Nothingfor EulerianRadCacheT:Properties.RadiationCachefor P1;NothingotherwiseBCStateT: per-boundary-tag state container (NamedTuple)StateCacheT:StateCache{NC, Tu, Mode}— typed mirror of the live state populated at every residual entry
Pyrolysis.Problem.to_problem_def — Function
to_problem_def(problem::PyrolysisProblem; use_ale=false,
radiation_model=NO_RADIATION,
radiation_intensity=0.0,
radiation_kirchhoff=true,
energy_form=:standard,
use_transpiration_bc=false,
use_blowing_correction=false) -> ProblemDefLift a user-facing PyrolysisProblem into the immutable ProblemDef. The returned object shares the underlying material/BC/initial-condition data with the input but no longer carries any mutable mesh state.
radiation_kirchhoff records the surface-transmissivity convention for the Beer–Lambert injection: true (the default) means radiation_intensity is the bare incident flux and the live surface-cell ε_eff is applied at injection (Kirchhoff α = ε); false means the RadiativeFluxBC's explicit absorptivity is already folded into the intensity. solve() derives it from the top thermal BC.
use_blowing_correction (off by default) rescales every ConvectiveBC in the surface energy balance by the Couette-film blowing factor h → h·B/(e^B − 1), B = ṁ″·c_p,g/h, built from the outward gas mass flux at each boundary face. This is the momentum-side companion of the transpiration enthalpy term and is fully compatible with the default Sconv-only convention (it modifies the convective coefficient, not the gas enthalpy accounting). At peak burning (B ≳ 1) it suppresses convective cooling substantially — e.g. B = 4 gives heff/h = 0.075. See Discretization.blowing_factor.
Enabling blowing alone on a case calibrated without it (and without flame heat-flux feedback) will raise T_s and MLR; blowing and flame feedback partially cancel and should be introduced together before any recalibration pass.
The gas-generation enthalpy sink is consistent only when every reaction heat h is referenced to the T_ref (≈298.15 K) ambient datum. With DSC-style heats quoted at the local reaction temperature (the usual interpretation of calibrated / ThermaKin / Gpyro heats), the gas products' sensible enthalpy is already inside h(T) and the sink subtracts it a second time (~0.7–1.2 MJ per kg of volatiles at 900 K). Under DSC-style heats the default :standard is the exact form. A one-time warning is emitted when the sink is selected. See compute_gas_generation_sink!.
Pyrolysis.Problem.build_workspace — Function
build_workspace(problem::PyrolysisProblem, def::ProblemDef;
min_thickness=1e-6,
ale_limiter=VANLEER, scalar_eltype::Type=Float64) -> WorkspaceBuild a Workspace that owns every per-step cache and work array used by the unified residual. Each cache is sized to the current mesh and component count.
scalar_eltype is the live state's eltype (Tu parameter) — Float64 for the canonical workspace solve() allocates; ForwardDiff.Dual{...} for the AD workspace allocated lazily by an AD Jacobian backend.
The mesh carried by the workspace is the same mutable Mesh1D referenced by problem. The mesh is read-only during residual / Jacobian assembly; live state lives in ws.state_cache and is written back to the mesh only between accepted timesteps via StepUpdateCallback.
Pyrolysis.Problem.build_ad_workspace — Function
build_ad_workspace(def::ProblemDef, ws::Workspace, ::Type{Tu}) -> WorkspaceBuild a parallel Workspace{NC, Mode, Tu} whose state cache, property cache, RHS cache, and physics work arrays carry scalar eltype Tu (typically ForwardDiff.Dual{Tag, Float64, N}), sharing the canonical workspace's mesh, layout, boundary IDs, and runtime settings.
Used by AD Jacobian backends to keep a Dual-typed workspace alongside the Float64 one — the residual then dispatches on eltype(ws.dT_work) == Tu end-to-end without per-call reallocation.
The mesh is shared (same identity), reflecting the read-only mesh contract during residual / Jacobian assembly.
Pyrolysis.Residual.residual! — Function
Unified ODE right-hand side for the pyrolysis package.
function residual!(du, u, def::ProblemDef, t, ws::Workspace) -> nothingThis is the single RHS for all non-P1 simulations. The trait dispatch chain is:
residual!(du, u, def, t, ws)
→ _residual!(du, u, def, t, ws, mesh_mode(def.mode))
::Eulerian → Eulerian path
::ALE → ALE pathInside each path, transport is dispatched via transport_mode(def.mode) (Fickian vs DarcyFick) and the χ block is included iff chi_mode(def.mode) isa WithChi.
P1 radiation has its own residual in Pyrolysis.Experimental.RadiationP1 and is not routed through this entry point.
Contract
defis immutable — safe to share across threads / sweep workers.wsis mutable — one per worker.tis a plainReal; the kernel must accept whatever scalar typedu/ucarry (Float64,ForwardDiff.Dual, …).
State-vector layout and accessors
Pyrolysis.Problem.state_length — Function
state_length(l::StateLayout) -> IntReturn the total length of the state vector encoded by this layout.
Pyrolysis.Problem.create_state_vector — Function
create_state_vector(layout::StateLayout, mesh) -> Vector{Float64}Allocate a fresh state vector sized to layout and pack the current mesh state into it. Used by solve() to build u0.
Pyrolysis.Problem.pack_state! — Function
pack_state!(u, mesh, layout::StateLayout) -> uPack the mesh's cell_states (and node positions when ALE; χ when WithChi) into the flat ODE state vector u. Mode is read from the layout's type parameter.
Pyrolysis.Problem.update_mesh_from_state! — Function
update_mesh_from_state!(mesh, u::AbstractVector{Float64}, layout::StateLayout) -> meshMirror an integrator-accepted Float64 state vector into the mutable mesh. Called by StepUpdateCallback between timesteps so AMR, depletion checks, and post-processing read a consistent mesh.
This function is not invoked during residual / Jacobian assembly — those are pure functions of u and the typed state_cache. The mesh is read-only during assembly.
Pyrolysis.Problem.block_view — Function
block_view(u, l::StateLayout, ::Val{:T}) -> view of T block
block_view(u, l::StateLayout, ::Val{:ξ}, j::Int) -> view of component j
block_view(u, l::StateLayout, ::Val{:z}) -> view of node-position block (ALE)
block_view(u, l::StateLayout, ::Val{:χ}) -> view of pyrolysis-progress block (WithChi)Compile-time-dispatched view into the named block of u. Returns an empty view when the block does not exist for the layout's mode.
Pyrolysis.Problem.get_temperature — Function
get_temperature(u, layout, i) -> RealPyrolysis.Problem.get_concentration — Function
get_concentration(u, layout, i, j) -> RealPyrolysis.Problem.get_node_position — Function
get_node_position(u, layout, i) -> RealOnly valid for ALE layouts.
Pyrolysis.Problem.get_node_positions — Function
get_node_positions(u, layout) -> viewView of node positions for ALE layouts.
Pyrolysis.Problem.get_thickness_from_state — Function
get_thickness_from_state(u, layout) -> RealCompute current domain thickness z_top - z_bottom from the ALE state. Returns 0 for Eulerian layouts.
Pyrolysis.Residual.column_chi_bar_from_state — Function
column_chi_bar_from_state(u, layout, mesh) -> RealMass-weighted column-averaged χ̄. AD-Dual safe (numerator carries duals, denominator from the immutable m_dry_solid_initial). Thin wrapper over the single implementation _chi_bar_from_state, which also returns the column dry-solid mass used by update_cross_section_area!.
Pyrolysis.Residual.cross_section_area_from_state — Function
cross_section_area_from_state(mesh, material, u, layout) -> RealCurrent column cross-sectional area implied by the χ block. Returns mesh.cross_section_area_initial for identity-law materials.