Crucible2.5D · native laboratory notes

Crucible2.5D · Thermal comparison routes

Separate display exposure from simulation cooling, and inspect the optional shared-face heat exchange.

These additions are opt-in. Existing scenes, material laws, legacy branches and the default rendering remain available. This is a comparison bench for specific numerical and presentation choices, not a claim that the whole sandbox now predicts physical heat transfer.

The recording presets now also start with the familiar fire transfer. Material beauty explicitly restores it after using the temperature diagnostic. Enable Appearance → Thermal capture & comparison → Alternate thermal display to try the alternative exposure/threshold mapping; Temperature still enables its diagnostic palettes. This corrects the original recording preset defaults, which enabled the alternative automatically despite the otherwise opt-in design.

Capture without changing the experiment

The old gas drawing starts at 305 K by default. It maps a broad temperature range to additive orange/yellow light. A room full of mildly heated gas can therefore look like a room full of flames, independently of what its particles are doing. The original Fire Start Temp / Temp Range / Softness controls remain.

The optional Thermal capture route in SDFRenderer::thermal adds:

All of this is display-only. Threshold, exposure and palettes never modify the thermal buffers. The gas/SDF field overlay is covered; MPM particle appearance, explosion decals, bloom and heat-haze passes have their own controls. These visuals are not calibrated radiometry. Even the 480 K threshold is a convenient capture parameter, not a claim that air visibly incandesces there.

For a restrained rocket recording: enable the route, try 480–650 K, -1 to -2 EV, and 0.5–0.8 saturation. First compare the temperature field. Do not cool the simulation merely to hide a bright color ramp.

A shared thermal face, rather than two separately clipped updates

For fixed cell volume, the heat-like quantity used by the scalar diffusion model is E_i = C_i T_i. Its pair exchange should satisfy

C_i (T_i' - T_i) = K_ij dt / dx² (T_j - T_i)
C_j (T_j' - T_j) = K_ij dt / dx² (T_i - T_j)

The equal and opposite terms cancel if both cells use the same K_ij. The previous shader individually clips K_ij dt / (dx² C_i) to 0.24. At unlike heat capacities, one side can clip while the other does not. This keeps each temperature bounded but no longer cancels capacity-weighted energy.

The optional Symmetric thermal flux route limits the shared face first:

q_ij = min(max(K_ij, 0) dt / dx², 0.24 min(C_i, C_j))
T_i' = T_i + sum_j q_ij / C_i (T_j - T_i)

The same q_ij is used by both sides. Four nonnegative coefficients each at most 0.24 make the step a convex combination, and each interior face cancels in sum C_i T_i. There are no extra GPU passes or new storage. This follows the shared-face conservation principle of cell-centered finite volumes.

Limits: limiting still reduces the effective conductivity at an excessively large timestep. It preserves a local ledger; it does not make that slowed diffusion physically time-accurate. Substepping or an implicit solve would be the next comparison. Existing interface coefficients remain authored, including air/solid contact transfer. Domain borders are pinned to ambient and exchange energy with an implicit reservoir. Cooling, sources, temperature clamps, semi-Lagrangian advection and MPM/SPH transfers are separate from this invariant. Do not describe the full simulation as globally energy-conserving.

The existing physically_based_heat flag is currently an inactive shader uniform: its older two-path distinction had already been removed before this pass. The new symmetric toggle is the explicit, functioning comparison.

Cold-air reservoir with an explicit time constant

The existing air cooling already uses exponential relaxation. Its effective rate is reduced by an authored smoke/fresh-vapor/combustion-hold term. That is a useful look-development choice but makes a requested cooling rate depend on what is burning in the cell.

The optional Thermal bath replaces that air cooling term with

T' = T_bath + (T - T_bath) exp(-dt / tau)

This is the exact local update for the frozen-reservoir ODE dT/dt = -(T - T_bath)/tau, a Newton cooling model. One tau leaves about 37% of the initial temperature gap; three leave about 5%. It is monotonic, works for heating as well as cooling, and cannot overshoot the reservoir merely because the frame timestep is large. Smoke does not protect a cell from this selected route.

Limits: this represents an external thermostat or unresolved exchange with surroundings, not a conservative gas model or a resolved fan. It acts on air; SDF solids respond through conduction and MPM/SPH matter through existing coupling. Strong combustion sources can defeat a weak bath. Bath temperature is clamped to the existing solver safety band [max(ambient - 100, 0), 2000] K; tau has a 0.001 s numerical floor. Changing the display exposure is preferable when the goal is only a less saturated recording.

Recording comparisons

  1. Warm room versus flame: pause a heated scene and toggle Thermal capture. Show the temperature palette beside the natural drawing. The state is unchanged; only the meaning of the color changes.
  2. Quench after a heat pulse: stop the heater, select a bath near ambient, and compare tau = 0.2, 0.5, 2.0 s. Keep the scene and camera identical. This demonstrates an external cooling model rather than new chemistry.
  3. Unequal heat capacities: place a hot solid beside cool air; use a large enough conductivity/timestep to exercise the limiter. Compare symmetric flux OFF/ON. The strongest evidence here is the numerical ledger below; the visual difference can be subtle under low, already-stable coefficients.

Crust, burning, annealing and melting remain the existing material behaviors. The added routes do not turn their authored thresholds, damage and shrinkage rules into calibrated constitutive models. Their value is to expose which layer changed: scalar transport, environmental cooling, or only the picture.

Retained verification

tests/thermal_comparison_test.cpp dispatches the actual production compute shader and draws the actual production display shader in a hidden OpenGL context. It checks:

The display comparison now covers eight flame/terrain fixtures: RGBA32F and RGBA8 targets, terrain lighting off/on, and constant/varying smoke. It checks that flames and temperature-driven terrain lighting are actually present, and that changing the dormant alternate exposure has no effect. These are direct production SDF/fire shader draws, not the complete particle, bloom, heat-haze and final grading pipeline. The original comparison covered one RGBA32F fixture with terrain lighting off.

Run from the source root: thermal_comparison_test.exe [baseline_directory]. The optional directory contains euler_diffuse_temp.comp and sdf_draw.frag from checkpoint 8baca7b. It tests the local changes; it is not a certificate of the sandbox's complete thermal, chemistry, contact or fracture behavior.

Measured on the local RTX 4080 Laptop GPU on 2026-09-19: 871 checks passed. This is the retained original test result, before the expanded display fixtures. For the controlled single hot solid cell with capacity 8 beside air of capacity 1, the initial excess sum C(T-300) is 2400. The old individually clipped step loses 912 units; the shared-face step loses 0 at the reported precision. Both the disabled thermal update and disabled capture image matched checkpoint 8baca7b exactly in the retained GPU cases. These numbers describe the test fixture, not the whole scene's energy balance.

On 2026-09-20 the expanded suite passed 950 checks, including exact matches in all eight fire/terrain fixtures. The retained local energy result remains unchanged. See regression_review_20260920.md for the separate native heat-tool false-blast reproduction and the limits of the comparison with older binaries.